Removing an element from a library - powershell

I have a library in a list which contains a document I want to delete. This has to be done for site collections, so I decided to make a script do it.
#Set the Error Action
$ErrorActionPreference = "Stop"
Try{
$customers = $SiteURL.GetFolder
foreach ($customer in $customers){
#customer is a list that contains the library sitepages
$customerlists = $customer.List.TryGetList("Site Pages")
#correctitem is the document item that I want to remove
$correctitem = $customerlists.List.TryGetList("123")
if ($correctitem){
#delete it (havent gotten here yet)
}
}
}
catch {
Write-Host $_.Exception.Message -ForegroundColor Red
}
finally {
#Reset the Error Action to Default
$ErrorActionPreference = "Continue"
}
However, I cant get it to work. The URL works, but the customers variable, when I try to Write-Host it does not display anything.
Any ideas?

If you want to delete a document from a document library, the PowerShell script below for your reference:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$site = new-object Microsoft.SharePoint.SPSite("http://sharepoint-site-url")
$web = $site.openweb()
$list=$web.Lists["Site Pages"]
$listItems = $list.Items
$listItemsTotal = $listItems.Count
Write-Host $listItemsTotal
for ($x=$listItemsTotal-1;$x -ge 0; $x--)
{
if($listItems[$x].name.Contains("123")) # file refers to the name of the document
{
Write-Host("DELETED: " + $listItems[$x].name)
$listItems[$x].Delete()
}
}

Related

How do I make this PowerShell script 'loop' tru multiple arrays?

This PowerShell script works (not mine, I found it - data has been changed to dummy data here) - it Syncs Documents from a SharePoint Site to the users OneDrive:
#region Functions
function Sync-SharepointLocation {
param (
[guid]$siteId,
[guid]$webId,
[guid]$listId,
[mailaddress]$userEmail,
[string]$webUrl,
[string]$webTitle,
[string]$listTitle,
[string]$syncPath
)
try {
Add-Type -AssemblyName System.Web
#Encode site, web, list, url & email
[string]$siteId = [System.Web.HttpUtility]::UrlEncode($siteId)
[string]$webId = [System.Web.HttpUtility]::UrlEncode($webId)
[string]$listId = [System.Web.HttpUtility]::UrlEncode($listId)
[string]$userEmail = [System.Web.HttpUtility]::UrlEncode($userEmail)
[string]$webUrl = [System.Web.HttpUtility]::UrlEncode($webUrl)
#build the URI
$uri = New-Object System.UriBuilder
$uri.Scheme = "odopen"
$uri.Host = "sync"
$uri.Query = "siteId=$siteId&webId=$webId&listId=$listId&userEmail=$userEmail&webUrl=$webUrl&listTitle=$listTitle&webTitle=$webTitle"
#launch the process from URI
Write-Host $uri.ToString()
start-process -filepath $($uri.ToString())
}
catch {
$errorMsg = $_.Exception.Message
}
if ($errorMsg) {
Write-Warning "Sync failed."
Write-Warning $errorMsg
}
else {
Write-Host "Sync completed."
while (!(Get-ChildItem -Path $syncPath -ErrorAction SilentlyContinue)) {
Start-Sleep -Seconds 2
}
return $true
}
}
#endregion
#region Main Process
try {
#region Sharepoint Sync
[mailaddress]$userUpn = cmd /c "whoami/upn"
$params = #{
#replace with data captured from your sharepoint site.
siteId = "{11111111-1111-1111-11111111111111111}"
webId = "{22222222-2222-2222-22222222222222222}"
listId = "{33333333-3333-3333-33333333333333333}"
userEmail = $userUpn
webUrl = "https://somecompany.sharepoint.com/sites/graphics"
webTitle = "graphics"
listTitle = "Documents"
}
$params.syncPath = "$(split-path $env:onedrive)\$($userUpn.Host)\$($params.webTitle) - $($Params.listTitle)"
Write-Host "SharePoint params:"
$params | Format-Table
if (!(Test-Path $($params.syncPath))) {
Write-Host "Sharepoint folder not found locally, will now sync.." -ForegroundColor Yellow
$sp = Sync-SharepointLocation #params
if (!($sp)) {
Throw "Sharepoint sync failed."
}
}
else {
Write-Host "Location already syncronized: $($params.syncPath)" -ForegroundColor Yellow
}
#endregion
}
catch {
$errorMsg = $_.Exception.Message
}
finally {
if ($errorMsg) {
Write-Warning $errorMsg
Throw $errorMsg
}
else {
Write-Host "Completed successfully.."
}
}
#endregion
But I need do this for several SharePoint Sites, but I'd like to avoid having to run 10 different PowerPoint Scripts, if possible.
What I've tried:
I've made an array where I've defined the variables for all the sites, like this (with the correct data):
$params = #(
[pscustomobject]#{
siteId = "SiteID";
webId = "webId";
listId = "listId";
userEmail = $userUpn;
webUrl = "webUrl";
webTitle = "webTitle";
listTitle = ""Documents"
}
[Repeated for each Site]
)
I've then altered this part of the original code, to do a ForEach loop:
#region Main Process
try {
#region Sharepoint Sync
[mailaddress]$userUpn = cmd /c "whoami/upn"
$params | ForEach-Object {
$params.syncPath = "$(split-path $env:onedrive)\$($userUpn.Host)\$($params.webTitle) - $($Params.listTitle)"
Write-Host "SharePoint params:"
$params | Format-Table
if (!(Test-Path $($params.syncPath))) {
Write-Host "Sharepoint folder not found locally, will now sync.." -ForegroundColor Yellow
$sp = Sync-SharepointLocation #params
if (!($sp)) {
Throw "Sharepoint sync failed."
}
}
else {
Write-Host "Location already syncronized: $($params.syncPath)" -ForegroundColor Yellow
}
}
#endregion
}
But it's not working as expected: Only the first Site in my Array is synced.
I'm a PowerShell beginner, so please help me: What am I doing wrong?
Thanks!
When looping through all the objects inside the $params array using a ForEach-Object, the object that you want to reference will be $_ .. not $params. So, where you are referencing $params.webTitle, etc., you should be referencing $_.webTitle, $_.listTitle, etc.
If you're new to Powershell, I would recommend you setting a variable like $paramObj for each instance using foreach ($parmObj in $params) {...} to make it clear what you are referencing, making the code look like:
# replaces the ForEach-Object
ForEach ($paramObj in $params) {
Write-Host "SharePoint params:"
$paramObj | Format-Table
# i didn't see a syncPath in your $params custom object.. better if you set a local variable
$syncPath = "$(split-path $env:onedrive)\$($userUpn.Host)\$($paramObj.webTitle) - $($paramObj.listTitle)"
if (!(Test-Path $($syncPath))) {
Write-Host "Sharepoint folder not found locally, will now sync.." -ForegroundColor Yellow
$sp = Sync-SharepointLocation #paramObj
if (!($sp)) {
Throw "Sharepoint sync failed."
}
}
else {
Write-Host "Location already syncronized: $syncPath" -ForegroundColor Yellow
}
}

Adding SP group to a newly created Person or Group Column via Powershell

I am creating a new column called "TEST123" which is a Person Or Group and wanted to also update "Choose from" option from All users to SP group called TESTAccounts.
I have got the following script to create the column but not sure how to make the above changes :
Function Add-FieldToList($SiteURL,$ListName, $FieldName, $FieldType, $IsRequired){
$ErrorActionPreference = "Stop"
Try{
$List = (Get-SPWeb $SiteURL).Lists.TryGetList($ListName)
if($List -ne $null)
{
if(!$List.Fields.ContainsField($FieldName))
{
$List.Fields.Add($FieldName,$FieldType,$IsRequired)
$List.Update()
$View = $List.DefaultView # OR $List.Views["All Items"]
$View.ViewFields.Add($FieldName)
$View.Update()
write-host "New Column '$FieldName' Added to the List!" -ForegroundColor Green
}
else
{
write-host "Field '$FieldName' Already Exists in the List" -ForegroundColor Red
}
}
else
{
write-host "List '$ListName' doesn't exists!" -ForegroundColor Red
}
}
catch {
Write-Host $_.Exception.Message -ForegroundColor Red
}
finally {
$ErrorActionPreference = "Continue"
}
}
$SiteURL="http://TESTURL"
$ListName = "NEWList"
$FieldType = [Microsoft.SharePoint.SPFieldType]::User
$FieldName="TEST123"
$IsRequired = $False
Add-FieldToList $SiteURL $ListName $FieldName $FieldType $IsRequired
You could use the follow powershell set people column choose from certain group:
$SPSite = Get-SPSite 'http://sp'
#Open you web
$OpenWeb = $SpSite.OpenWeb();
#Open Your List
$List = $OpenWeb.Lists["test2"];
$column = $list.Fields["user"]#change to your column name
$column.SelectionGroup=15 #group id you want
$column.Update()

Download files from set of folders on FTP/SFTP server with WinSCP .NET assembly in PowerShell and email results

I have this PowerShell script that I'm working on. CSV file is imported to get source and destination paths. The goal is to move files from a SFTP/FTP server into a destination and send an email report.
Task scheduler will run this code every hour. And if there's a new file, as email will be sent out.
It's almost done, but two things are missing:
Check if the file already exists and Body email seems empty: Getting the following error: Cannot validate argument on parameter 'Body'. The argument is null or empty. Provide an argument that is not
null or empty, and then try the command again.
I would like some assistance on how to check if the file exists and how to get this email if a new file was dropped and copied to the destination list
$SMTPBody = ""
$SMTPMessage = #{
"SMTPServer" = ""
"From" = ""
"To" = ""
"Subject" = "New File"
}
try {
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::sftp
HostName = ""
UserName = ""
Password = ""
PortNumber = "22"
FTPMode = ""
GiveUpSecurityAndAcceptAnySshHostKey = $true
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
$synchronizationResult = $session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$synchronizationResult.Check()
foreach ($download in $synchronizationResult.Downloads ) {
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$SMTPBody +=
"`n Files: $($download.FileName -join ', ') `n" +
"Current Location: $($_.Destination)`n"
Send-MailMessage #SMTPMessage -Body $SMTPBody
}
$transferResult =
$session.GetFiles($_.Source, $_.Destination, $False, $transferOptions)
#Find the latest downloaded file
$latestTransfer =
$transferResult.Transfers |
Sort-Object -Property #{ Expression = { (Get-Item $_.Destination).LastWriteTime }
} -Descending |Select-Object -First 1
}
if ($latestTransfer -eq $Null) {
Write-Host "No files found."
$SMTPBody += "There are no new files at the moment"
}
else
{
$lastTimestamp = (Get-Item $latestTransfer.Destination).LastWriteTime
Write-Host (
"Downloaded $($transferResult.Transfers.Count) files, " +
"latest being $($latestTransfer.FileName) with timestamp $lastTimestamp.")
$SMTPBody += "file : $($latestTransfer)"
}
Write-Host "Waiting..."
Start-Sleep -Seconds 5
}
finally
{
Send-MailMessage #SMTPMessage -Body $SMTPBody
# Disconnect, clean up
$session.Dispose()
}
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
}
I believe your code has more problems than you think.
Your combination of SynchronizeDirectories and GetFiles is suspicious. You first download only the new files by SynchronizeDirectories and then you download all files by GetFiles. I do not think you want that.
On any error the .Check call will throw and you will not collect the error into your report.
You keep sending partial reports by Send-MailMessage in the foreach loop
This is my take on your problem, hoping I've understood correctly what you want to implement:
$SMTPBody = ""
Import-Csv -Path "FILESOURCE.csv" -ErrorAction Stop | foreach {
Write-Host "$($_.Source) => $($_.Destination)"
$SMTPBody += "$($_.Source) => $($_.Destination)`n"
$synchronizationResult =
$session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$downloaded = #()
$failed = #()
$latestName = $Null
$latest = $Null
foreach ($download in $synchronizationResult.Downloads)
{
if ($download.Error -eq $Null)
{
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$downloaded += $download.FileName
$ts = (Get-Item $download.Destination).LastWriteTime
if ($ts -gt $latest)
{
$latestName = $download.FileName;
$latest = $ts
}
}
else
{
Write-Host "File $($download.FileName) download failed" -ForegroundColor Red
$failed += $download.FileName
}
}
if ($downloaded.Count -eq 0)
{
$SMTPBody += "No new files were downloaded`n"
}
else
{
$SMTPBody +=
"Downloaded $($downloaded.Count) files:`n" +
($downloaded -join ", ") + "`n" +
"latest being $($latestName) with timestamp $latest.`n"
}
if ($failed.Count -gt 0)
{
$SMTPBody +=
"Failed to download $($failed.Count) files:`n" +
($failed -join ", ") + "`n"
}
$SMTPBody += "`n"
}
It will give you a report like:
/source1 => C:\dest1`
Downloaded 3 files:
/source1/aaa.txt, /source1/bbb.txt, /source1/ccc.txt
latest being /source1/ccc.txt with timestamp 01/29/2020 07:49:07.
/source2 => C:\dest2
Downloaded 1 files:
/source2/aaa.txt
latest being /source2/aaa.txt with timestamp 01/29/2020 07:22:37.
Failed to download 1 files:
/source2/bbb.txt
To check and make sure the csv file exists before you process the entire thing, you can use the Test-Path,
...
if (!(Test-Path D:\FileSource.csv)) {
Write-Output "No File Found"
$SMTPBody += "There are no new files at the moment"
return; # Dont run. file not found. Exit out.
}
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
...
and for the Body error you are getting, it is coming from the finally loop because there are cases where $SMTPBody would be null. This will no longer be an issue because $SMTPBody will have some text when file is not found at the beginning.
Even though you are using return in the if statement to check if the file exists, finally will always get executed. Since we updated $smtpbody, your Send-MailMessage will no longer error out.
Update
If you want to check if the file you are downloading already exists, you can use the if statement like this,
foreach ($download in $synchronizationResult.Downloads ) {
if (!(Test-Path Join-Path D: $download.FileName) {
$SMTPBody += "File $($download.Filename) already exists, skipping."
continue # will go to the next download...
}
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
...
If you do get the error regarding body, thats mostly because your script came across an exception and was sent straight over to finally statement. Finally statement sends the email with empty body because it was never set (due to exception). I would recommend using the debugger (step through) and see which step causes the exception and look into adding steps to make sure script doesnt fail.

Adding Read-Only-permissions to a list for a Sharepoint Group using Powershell CSOM

I am trying to add read-only-permissions to a specific group called "Students" for a list I have created called "Quiz". I have to use PowerShell CSOM, but in every other tutorial I've been through, .NET server types have been used, which is not applicable to my code.
Code:
$ListName = "Quiz"
$PermissionLevel = "Read Only"
$web = $ctx.Web
$lists = $web.Lists
$ctx.Load($lists)
$ctx.ExecuteQuery()
foreach($list in $lists)
{
if($list.Title -eq $ListName)
{
$listId = $list.Id
}
}
$list = $lists.GetById($listId)
$ctx.Load($list);
$ctx.ExecuteQuery();
Write-Host "List:" $List.Title -foregroundcolor Green
if ($list -ne $null)
{
$groups = $web.SiteGroups
$ctx.Load($groups)
$ctx.ExecuteQuery()
foreach ($SiteGroup in $groups) {
if ($SiteGroup.Title -match "Students")
{
write-host "Group:" $SiteGroup.Title -foregroundcolor Green
$GroupName = $SiteGroup.Title
$builtInRole = $ctx.Web.RoleDefinitions.GetByName($PermissionLevel)
$roleAssignment = new-object Microsoft.SharePoint.Client.RoleAssignment($SiteGroup)
$roleAssignment.Add($builtInRole)
$list.BreakRoleInheritance($True, $False)
$list.RoleAssignments.Add($roleAssignment)
$list.Update();
Write-Host "Successfully added <$GroupName> to the <$ListName> list in <$site>. " -foregroundcolor Green
}
else
{
Write-Host "No Students groups exist." -foregroundcolor Red
}
}
}
My error is in
$roleAssignment = new-object Microsoft.SharePoint.Client.RoleAssignment($SiteGroup)
, where I'm recieving the error
Cannot find an overload for "RoleAssignment" and the argument count: "1".
Most tutorials use
$roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($SiteGroup)
which I CAN NOT USE.
How can I complete my code?
P.S. I know my code is a bit messy, but I've been spending too much time trying to find a solution, and my code has greatly reduced in quality over the past hours. Sorry for that.
I dont have sharepoint to test but you are getting the error on roleassignments because the method you are calling takes 2 arguments.
anyway you can try something along these lines:
$roleAssignment = New-Object microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$roleAssignment.Add($builtinRole)
$ctx.Load($list.RoleAssignments.Add($sitegroup, $roleAssignment))
Unnie on stackexchange helped me by providing the following code. I should have used microsoft.SharePoint.Client.RoleDefinitionBindingCollection instead of Microsoft.SharePoint.Client.RoleAssignment.
# Get the list by Title and load.
$web = $ctx.Web
$list = $web.Lists.GetByTitle("Quiz")
$ctx.Load($list)
# Load in list of groups on the current web.
$groups = $web.SiteGroups
$ctx.Load($groups)
$ctx.ExecuteQuery()
$listTitle = $list.Title
foreach($group in $groups)
{
if($group.Title -eq "Students")
{
$roleAssignment = $null
# Get the group and load into context to be used.
$StudentsGrp = $groups.GetById($group.Id)
$ctx.Load($StudentsGrp)
$ctx.ExecuteQuery()
# Break inheritance on the list and remove existing permissons.
$list.BreakRoleInheritance($false, $true)
# Get the permissions role for 'Read'
$reader = $web.RoleDefinitions.GetByName("Read")
# Create a role assignment and apply the 'read' role.
$roleAssignment = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$roleAssignment.Add($reader)
# Apply the permission roles to the list.
$ctx.Load($list.RoleAssignments.Add($StudentsGrp, $roleAssignment))
$list.Update()
$ctx.ExecuteQuery()
}
}
This works! :-)

adding file to document library

Here is my document library looks like. I have 2 content types with my document library. 1 content type is based on Document and the other content type is based on "Link to a document" type.
I am trying to upload some files using powershell script. I am using a csv file to read line by line and upload files to document library. I am getting error on $i.fileNameASPX. Powershell tells me that Missing expression after , . So what to do.
if($i.ContentType = "LegalLink2Document")
{
$itemType = $docLibrary.ContentTypes[$i.ContentType]
$newFile = $docLibrary.RootFolder.Files.Add($i.fileNameASPX, UTF8Encoding.UTF8.GetBytes(builder.ToString()), $true)
$theItem = $newFile.Item
$theItem["ContentTypeId"] = $itemType.Id
$itemUrl = ew-object Microsoft.SharePoint.SPFieldUrlValue()
$itemUrl.Url = $i.fileLinkUrl
$itemUrl.Descrition = $i.URLDESC
$theItem["URL"] = $itemUrl
}
Here is the Simple PowerShell script to upload files to a document library in SharePoint2013
http://soreddymanjunath.blogspot.in/2014/07/add-file-to-document-library-using.html
cls
asnp "*sh*"
$url=Read-Host "Enter Site Url"
$web=Get-SPWeb -Identity $url
if($web)
{
try
{
$list = $web.Lists.TryGetList("Documents")
$files = Get-ChildItem -Path "D:\M" -Force -Recurse
foreach ($file in $files)
{
$stream = $file.OpenRead()
$done= $list.RootFolder.Files.Add($file.Name, $stream, $true)
Write-Host $done.Name "Uploaded into the Site" -BackgroundColor Green
}
}
catch
{
$ErrorMessage = $_.Exception.Message
Write-Host $ErrorMessage
}
}
else
{
Write-Host "Site Doesn't exist"
}
$list.Update();