Upload multiple files from multiple folders to SharePoint online using Powershell - powershell

Fairly new to both SharePoint online and Powershell and thought this would be a pretty simple task, but I'm reaching out for help.
I have a client who has photos stored in multiple folders in a file share and they want to move this to SharePoint. They want to use the folder name of where the file exits as metadata to make searching easier.
This is the script I am using and not having much luck.
$connection = Connect-PnPOnline https://somecompany.sharepoint.com -Credentials $me -ReturnConnection
$files = Get-ChildItem "F:\some data" -Recurse
foreach ($file in $files)
{Add-PnPFile -Path $file.FullName -Folder Photos -Values #{"Title" = $file.Name;} -Connection $connection}
Issue I am having, is that this does not recurse the folders and comes back with " Local file not found"
If I can get that working, I can move onto getting the current folder name as a variable into metadata.
I'm pretty sure that this will be a simple task for experts, but alas that I am not.Any help will be greatly appreciated.
Thanks
Jassen

This seems to work for me, so will answer this. Happy for comments if there is an easier way or cleaner and also if anyone knows how to go 1 more layer deeper.
$connection = Connect-PnPOnline https://somecompany.sharepoint.com -Credentials $me -ReturnConnection
$LocalFolders = get-childitem -path "c:\test" | where-object {$_.Psiscontainer} | select-object FullName
foreach ($folder in $localfolders) {
$files = get-childitem -Path $folder.FullName -Recurse
foreach ($file in $files) {
$value1 = $file.Directory.Name
Add-PnPFile -Path $file.FullName -Folder Photos -Values #{"Title" = $file.Name;"SubCat" = $value1;} -Connection $connection
}
}

You could try below script, you need install pnp powershell.
function UploadDocuments(){
Param(
[ValidateScript({If(Test-Path $_){$true}else{Throw "Invalid path given: $_"}})]
$LocalFolderLocation,
[String]
$siteUrl,
[String]
$documentLibraryName
)
Process{
$path = $LocalFolderLocation.TrimEnd('\')
Write-Host "Provided Site :"$siteUrl -ForegroundColor Green
Write-Host "Provided Path :"$path -ForegroundColor Green
Write-Host "Provided Document Library name :"$documentLibraryName -ForegroundColor Green
try{
$credentials = Get-Credential
Connect-PnPOnline -Url $siteUrl -CreateDrive -Credentials $credentials
$file = Get-ChildItem -Path $LocalFolderLocation -Recurse
$i = 0;
Write-Host "Uploading documents to Site.." -ForegroundColor Cyan
(dir $path -Recurse) | %{
try{
$i++
if($_.GetType().Name -eq "FileInfo"){
$SPFolderName = $documentLibraryName + $_.DirectoryName.Substring($path.Length);
$status = "Uploading Files :'" + $_.Name + "' to Location :" + $SPFolderName
Write-Progress -activity "Uploading Documents.." -status $status -PercentComplete (($i / $file.length) * 100)
$te = Add-PnPFile -Path $_.FullName -Folder $SPFolderName
}
}
catch{
}
}
}
catch{
Write-Host $_.Exception.Message -ForegroundColor Red
}
}
}
UploadDocuments -LocalFolderLocation C:\Lee\Share -siteUrl https://tenant.sharepoint.com/sites/Developer -documentLibraryName MyDOc4

Related

How to remove large OneDrive folder or subfolder using PowerShell

I'm planning to delete my OneDrive subfolders using PowerShell but I'm unable to do it because the sub folders is prettry large, have too many depth and items so just wondering how should I modify my script so that I can delete it.
Under the Parent Folder, I used to have 5 Subfolders. I was able to delete the other 3 subfolders already but I wasn't able to do it for the remaining 2 because of the reason above.
Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll"
#Key File information for secure connection
$Global:adminUPN = "upn"
$Global:PasswordFile = "C:\scripts\LitHold-OneDrive\key-file\ODpw.txt"
$Global:KeyFile = "C:\scripts\LitHold-OneDrive\key-file\OD.key"
$Global:adminPwd = ""
$CurDate = Get-Date -Format "yyyy-MM-dd"
#Pwd Key Encryption File
$key = Get-Content $Global:KeyFile
$Global:adminPwd = Get-Content $Global:PasswordFile | ConvertTo-SecureString -Key $key
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Global:adminUPN, $Global:adminPwd
#Variables
$SiteURL = "OD URL"
$ServerRelativeUrl= "Documents/parentFolder"
Try {
#Get Credentials to connect
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Global:adminUPN, $Global:adminPwd)
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$ctx.Credentials = $Credentials
#Get the web from URL
$Web = $Ctx.web
$Ctx.Load($Web)
$Ctx.executeQuery()
#Get the Folder object by Server Relative URL
$Folder = $Web.GetFolderByServerRelativeUrl($ServerRelativeUrl)
$Ctx.Load($Folder)
$Ctx.ExecuteQuery()
#Call the function to empty Folder
Empty-SPOFolder $Folder
#Delete the given Folder itself
Write-host -f Green "Deleting Folder:"$Folder.ServerRelativeUrl
$Folder.Recycle() | Out-Null
$Ctx.ExecuteQuery()
}
Catch {
write-host -f Red "Error:" $_.Exception.Message
}
#Function to Delete all files and Sub-folders of a given Folder
Function Empty-SPOFolder([Microsoft.SharePoint.Client.Folder]$Folder)
{
Try {
#Get All Files from the Folder
$Ctx = $Folder.Context
$Files = $Folder.Files
$Ctx.Load($Files)
$Ctx.ExecuteQuery()
#Iterate through each File in the Root folder
Foreach($File in $Files)
{
#Delete the file
Write-Host -f Green "$File.Name"
$Folder.Files.GetByUrl($File.ServerRelativeUrl).Recycle() | Out-Null
$Folder.Files.GetByUrl($File.ServerRelativeUrl).DeleteObject() | Out-Null
Write-host -f Green "Deleted File '$($File.Name)' from '$($File.ServerRelativeURL)'"
}
$Ctx.ExecuteQuery()
#Process all Sub Folders of the given folder
$SubFolders = $Folder.Folders
$Ctx.Load($SubFolders)
$Ctx.ExecuteQuery()
#delete all subfolders
Foreach($Folder in $SubFolders)
{
#Exclude "Forms" and Hidden folders
#Call the function recursively to empty the folder
Empty-SPOFolder -Folder $Folder
#Delete the folder
Write-Host -f Green "$Folder.UniqueId"
#$Ctx.Web.GetFolderById($Folder.UniqueId).Recycle() | Out-Null
$Ctx.Web.GetFolderById($Folder.UniqueId).DeleteObject() | Out-Null
$Ctx.ExecuteQuery()
Write-host -f Green "Deleted Folder:"$Folder.ServerRelativeUrl
}
}
Catch {
write-host -f Red "Error: $Folder.UniqueId - $File.Name " $_.Exception.Message
}
}

Small Issues with powershell Script that loops over folders in Sharepoint

This Script loops over every folder in powershell and checks if the folder has the wildcard text and then outputs the file location towards the csv.
The error i get is : Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
Also some of the folders containing the wildcard. Mostly in level 3 - 4 - 5 it does not detect even tough in the output i see a folder containing the word.
Thanks in advance !
Import-Module PnP.PowerShell
#Config Parameters
$SiteURL= "*******/sites/Archief"
$Folder = "Gedeelde Documenten"
$Pagesize = "500"
Connect-PnPOnline -Url $SiteURL -Interactive ### Change to your specific site ###
#Output path
$outputPath = "C:\users\$env:USERNAME\Desktop\test.csv"
Function GetFolders($folderUrl)
{
$folderColl=Get-PnPFolderItem -FolderSiteRelativeUrl $folderUrl -ItemType Folder
# Loop through the folders
foreach($folder in $folderColl)
{
$newFolderURL= $folderUrl+"/"+$folder.Name
if ($newFolderURL -cnotlike "*archief*"){
write-host -ForegroundColor Green $folder.Name " - " $newFolderURL
$DocLibs = Get-PnPList | Where-Object {$_.BaseTemplate -eq 101}
#Loop thru each document library & folders
$results = #()
foreach ($DocLib in $DocLibs) {
if ($DocLib -cnotlike "*archief*"){
$AllItems = Get-PnPListItem -List $DocLib -Fields "FileRef", "File_x0020_Type", "FileLeafRef" -PageSize 500
#Loop through each item
foreach ($Item in $AllItems) {
if ($Item["FileRef"] -cnotlike "*archief*" -or $Item["FileLeafRef"] -cnotlike "*archief*" -and ($Item["File_x0020_Type"])){
Write-Host "File found. Path:" $Item["FileRef"] -ForegroundColor Green
#Creating new object to export in .csv file
$results += New-Object PSObject -Property #{
Path = $Item["FileRef"]
FileName = $Item["FileLeafRef"]
FileExtension = $Item["File_x0020_Type"]
}
}
}
}
}
GetFolders($newFolderURL)
}
}
}
$results | Export-Csv -Path $outputPath -NoTypeInformation
GetFolders($folder)
There are a few things wrong with your code. First of all, you first output the results to a CSV and after that call the function. Also you loop through the folders, and constantly create a new results object which is never outputted. lastly you call the function itself from within the function. You call GetFolders in a foreach loop within the GetFolders function.
There are a few ways to resolve this, for instance let the CSV be filled from inside the function and append new data.
Import-Module PnP.PowerShell
#Config Parameters
$SiteURL= "*******/sites/Archief"
$Folder = "Gedeelde Documenten"
$Pagesize = "500"
Connect-PnPOnline -Url $SiteURL -Interactive ### Change to your specific site ###
#Output path
$outputPath = "C:\users\$env:USERNAME\Desktop\test.csv"
Function GetFolders{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$FolderUrl,
[Parameter(Mandatory=$true)]
$OutputPath
)
$folderColl=Get-PnPFolderItem -FolderSiteRelativeUrl $folderUrl -ItemType Folder
# Loop through the folders
foreach($folder in $folderColl){
$newFolderURL= $folderUrl+"/"+$folder.Name
if ($newFolderURL -cnotlike "*archief*"){
write-host -ForegroundColor Green $folder.Name " - " $newFolderURL
$DocLibs = Get-PnPList | Where-Object {$_.BaseTemplate -eq 101}
#Loop thru each document library & folders
$results = #()
foreach($DocLib in $DocLibs){
if($DocLib -cnotlike "*archief*"){
$AllItems = Get-PnPListItem -List $DocLib -Fields "FileRef", "File_x0020_Type", "FileLeafRef" -PageSize 500
#Loop through each item
foreach ($Item in $AllItems) {
if ($Item["FileRef"] -cnotlike "*archief*" -or $Item["FileLeafRef"] -cnotlike "*archief*" -and ($Item["File_x0020_Type"])){
Write-Host "File found. Path:" $Item["FileRef"] -ForegroundColor Green
#Creating new object to export in .csv file
$results += New-Object PSObject -Property #{
Path = $Item["FileRef"]
FileName = $Item["FileLeafRef"]
FileExtension = $Item["File_x0020_Type"]
}
}
}
}
}
$results | Export-Csv -Path $outputPath -NoTypeInformation -Append
GetFolders($newFolderURL)
}
}
}
GetFolders -FolderUrl $Folder -OutputPath $outputPath
As you can see the output path is added to the function and the results are pushed to the CSV after every loop.
You could also let the results be created within the function and then returned to the initial call:
Import-Module PnP.PowerShell
#Config Parameters
$SiteURL= "*******/sites/Archief"
$Folder = "Gedeelde Documenten"
$Pagesize = "500"
Connect-PnPOnline -Url $SiteURL -Interactive ### Change to your specific site ###
#Output path
$outputPath = "C:\users\$env:USERNAME\Desktop\test.csv"
Function GetFolders{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
$FolderUrl
)
$folderColl=Get-PnPFolderItem -FolderSiteRelativeUrl $folderUrl -ItemType Folder
# Loop through the folders
$results = #()
foreach($folder in $folderColl){
$newFolderURL= $folderUrl+"/"+$folder.Name
if ($newFolderURL -cnotlike "*archief*"){
write-host -ForegroundColor Green $folder.Name " - " $newFolderURL
$DocLibs = Get-PnPList | Where-Object {$_.BaseTemplate -eq 101}
#Loop thru each document library & folders
foreach($DocLib in $DocLibs){
if($DocLib -cnotlike "*archief*"){
$AllItems = Get-PnPListItem -List $DocLib -Fields "FileRef", "File_x0020_Type", "FileLeafRef" -PageSize 500
#Loop through each item
foreach ($Item in $AllItems) {
if ($Item["FileRef"] -cnotlike "*archief*" -or $Item["FileLeafRef"] -cnotlike "*archief*" -and ($Item["File_x0020_Type"])){
Write-Host "File found. Path:" $Item["FileRef"] -ForegroundColor Green
#Creating new object to export in .csv file
$results += New-Object PSObject -Property #{
Path = $Item["FileRef"]
FileName = $Item["FileLeafRef"]
FileExtension = $Item["File_x0020_Type"]
}
}
}
}
}
GetFolders($newFolderURL)
}
}
return $results
}
GetFolders -FolderUrl $Folder | Export-Csv -Path $outputPath -NoTypeInformation
I would also recommend using '-Delimiter ";"' in your Export-CSV so that when you open the file in Excel it is automatically sorted.

powershell - check if folder exists before deleting it

I wanna delete all folders from out of a list (list.txt). Before deleting them, I want to check if they are still existing.
How can I do this task? Maybe with foreach or if? I am a newbie :)
And a second task: I have this list and I want to check for the folders which are still existent and write them into a new file.
$homedirectory = get-content -Path "c:\SCO_scripts\homedirectory_delete\list.txt"
if ( Test-Path -Path $homedirectory -PathType Container ) {
Remove-item $homedirectory -Force -recurse
}
or something like that?....
$homedirectory = get-content -Path "c:\SCO_scripts\homedirectory_delete\list.txt"
foreach ($folder in $homedirectory)
{
exists -Path $folder <---??? no idea how to make it :(
Add-Content -Path C:\SCO_scripts\homedirectory_delete\newFile.txt -Value $folder
}
**would you test this please **
$folders= Get-Content -Path C:\Temp\test.txt
foreach($i in $folders){
if (Test-Path -Path $i){
Write-Host $i exist
}
else{
Write-Host $i not exist
}
}
I have found a solution.
$folders= Get-Content -Path c:\SCO_scripts\homedirectory_delete\list.txt
foreach ($folder in $folders) {
$testpath = Test-Path $folder
if ($testpath -eq $true)
{
Write-Host "$folder Folder Exists" -ForegroundColor Green
Add-Content -Path C:\SCO_scripts\homedirectory_delete\newFile.txt -Value $folder
}
else
{
Write-Host "$folder is not Online" -ForegroundColor Red
}
}

check if a Deny permission already exists to a directory or not in PowerShell

I have been writing a script
Workflow:
Get list all of fixed disks ( except cdrom , floppy drive , usb drive)
to check if a path exists or not in PowerShell
to check if a Deny permission already exists to a directory or not in PowerShell
Set deny permission for write access for users
My question are :
1- After file exist control like below , also I want to check if a Deny permission already exists to a directory. ("$drive\usr\local\ssl")
If(!(test-path $path))
{
New-Item -ItemType Directory -Force -Path $path
}
2- there are about 1000 machines. How can I improve this script ?
Thanks in advance,
script :
$computers = import-csv -path "c:\scripts\machines.csv"
Foreach($computer in $computers){
$drives = Get-WmiObject Win32_Volume -ComputerName $computer.ComputerName | Where { $_.drivetype -eq '3'} |Select-Object -ExpandProperty driveletter | sort-object
foreach ($drive in $drives) {
$path = "$drive\usr\local\ssl"
$principal = "users"
$Right ="Write"
$rule=new-object System.Security.AccessControl.FileSystemAccessRule($Principal,$Right,"Deny")
If(!(test-path $path))
{
New-Item -ItemType Directory -Force -Path $path
}
try
{
$acl = get-acl $folder
$acl.SetAccessRule($rule)
set-acl $folder $acl
}
catch
{
write-host "ACL failed to be set on: " $folder
}
#### Add-NTFSAccess -Path <path> -Account <accountname> -AccessType Deny -AccessRights <rightstodeny>
}
}
The first thing I noticed is that in your code, you suddenly use an undefined variable $folder instead of $path.
Also, you get the drives from the remote computer, but set this $path (and try to add a Deny rule) on folders on your local machine:
$path = "$drive\usr\local\ssl"
where you should set that to the folder on the remote computer:
$path = '\\{0}\{1}$\usr\local\ssl' -f $computer, $drive.Substring(0,1)
Then, instead of Get-WmiObject, I would nowadays use Get-CimInstance which should give you some speed improvement aswell, and I would add some basic logging so you will know later what happened.
Try this on a small set of computers first:
Note This is assuming you have permissions to modify permissions on the folders of all these machines.
$computers = Import-Csv -Path "c:\scripts\machines.csv"
# assuming your CSV has a column named 'ComputerName'
$log = foreach ($computer in $computers.ComputerName) {
# first try and get the list of harddisks for this computer
try {
$drives = Get-CimInstance -ClassName Win32_Volume -ComputerName $computer -ErrorAction Stop |
Where-Object { $_.drivetype -eq '3'} | Select-Object -ExpandProperty driveletter | Sort-Object
}
catch {
$msg = "ERROR: Could not get Drives on '$computer'"
Write-Host $msg -ForegroundColor Red
# output a line for the log
$msg
continue # skip this one and proceed on to the next computer
}
foreach ($drive in $drives) {
$path = '\\{0}\{1}$\usr\local\ssl' -f $computer, $drive.Substring(0,1)
$principal = "users"
$Right = "Write"
if (!(Test-Path -Path $path -PathType Container)) {
$null = New-Item -Path $path -ItemType Directory -Force
}
# test if the path already has a Deny on write for the principal
$acl = Get-Acl -Path $path -ErrorAction SilentlyContinue
if (!$acl) {
$msg = "ERROR: Could not get ACL on '$path'"
Write-Host $msg -ForegroundColor Red
# output a line for the log
$msg
continue # skip this one and proceed to the next drive
}
if ($acl.Access | Where-Object { $_.AccessControlType -eq 'Deny' -and
$_.FileSystemRights -band $Right -and
$_.IdentityReference -like "*$principal"}) {
$msg = "INFORMATION: Deny rule already exists on '$path'"
Write-Host $msg -ForegroundColor Green
# output a line for the log
$msg
}
else {
$rule = [System.Security.AccessControl.FileSystemAccessRule]::new($Principal, $Right, "Deny")
# older PS versions use:
# $rule = New-Object System.Security.AccessControl.FileSystemAccessRule $Principal, $Right, "Deny"
try {
$acl.AddAccessRule($rule)
Set-Acl -Path $path -AclObject $acl -ErrorAction Stop
$msg = "INFORMATION: ACL set on '$path'"
Write-Host $msg -ForegroundColor Green
# output a line for the log
$msg
}
catch {
$msg = "ERROR: ACL failed to be set on: '$path'"
Write-Host $msg -ForegroundColor Red
# output a line for the log
$msg
}
}
}
}
# write the log
$log | Set-Content -Path "c:\scripts\SetAccessRuleResults.txt" -Force

How can i get a prompt to ask for the path, when using Get-ChildItem in PowerShell?

I'm rather new to PowerShell.
I found a few guides and mashed a little script together, though i do not seem to be able to set a prompt to ask for the source/destination.
The script is as following:
gci -path | Get-Random -Count 4 | mi -Destination C:\Temp
while(1) { sleep -sec 20; .\Power.ps1 }
For any response,
thanks in advance!
Use Read-Host:
Get-ChildItem -Path (Read-Host -Prompt 'Get path')
Here's an example, FWIW:
$source,$target = $null,$null
while ($source -eq $null){
$source = read-host "Enter source file name"
if (-not(test-path $source)){
Write-host "Invalid file path, re-enter."
$source = $null
}
elseif ((get-item $source).psiscontainer){
Write-host "Source must be a file, re-enter."
$source = $null
}
}
while ($target -eq $null){
$target = read-host "Enter source directory name"
if (-not(test-path $target)){
Write-host "Invalid directory path, re-enter."
$target = $null
}
elseif (-not (get-item $target).psiscontainer){
Write-host "Target must be a directory, re-enter."
$target = $null
}
}