Upload files to SharePoint Online as System Account - powershell

How do I upload files and create folders to SharePoint Online through PowerShell while keeping their metadata (Modified By) as "System Account" instead of having the files using my account on the metadata?
I know I can change the metadata for files by doing this:
$user = Get-PnPUser -ErrorAction Stop | ? Email -eq $email
$newFile["Author"] = $user.id
$newFile["Editor"] = $user.id
$newFile["Modified"] = $currentFile.LastWriteTimeUtc
$newFile["Created"] = $currentFile.CreationTimeUtc
$newFile.Update()
But how about folders? If I use CSOM with PowerShell (see below) I can't find a way to modify the metadata properties for "Author" and "Editor":
$CSOM_credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName, $cred.Password)
$CSOM_context = New-Object Microsoft.SharePoint.Client.ClientContext("https://tenanturl.sharepoint.com")
$CSOM_context.Credentials = $CSOM_credentials
$ParentFolder = $CSOM_context.web.GetFolderByServerRelativeUrl("Samples")
$newFolder = $ParentFolder.Folders.Add("New Folder")
$ParentFolder.Context.ExecuteQuery()
$CSOM_context.Load($newFolder)
$CSOM_context.ExecuteQuery()
If I use the PnP PowerShell (Add-PnPFolder), I have the same problem I can't get a way to modify the same metadata

You can use "SHAREPOINT\System" to set Author and Editor to System Account.
$Id = 1024
$item = Get-PnPListItem -List "listNmae" -Id $Id
Set-PnPListItem -List "listNmae" -Identity $Id -Values #{"Author" = "SHAREPOINT\System";
"Editor" = "SHAREPOINT\System";
"Modified" = $item.FieldValues.Modified
}
Also you can get System Account user using
$sysaccount = Get-PnPUser | ? {$_.LoginName -eq "SHAREPOINT\System"}

Related

Downloading SharePoint online list items' attachments locally will not allow me to open those files (seems the file are corrupted)

I have a SharePoint online list , and this list contain items with attachments. so i want to download all the list item attachems. so i wrote this PnP Power Shell sript:-
$ApprovalListItems = Get-PnPListItem -List "tickets" -PageSize 1000 -ScriptBlock { Param($items) $items.Context.ExecuteQuery()} | ForEach-Object {
$ctx = Get-PnPContext
$spSourceWeb= Get-PnPWeb
$ItemAttachmentURLPrefix = 'https://****.sharepoint.com/Lists/tickets/Attachments/'+$_['ID']+'/'
$attachmentCollection = ForEach-Object{Get-PnPProperty -ClientObject $_ -Property "AttachmentFiles"}
$AttachmentSeq = 0
ForEach($Attachment in $attachmentCollection)
{
Write-Host "`tDownloading Attachement: " $attachment $ItemAttachmentURLPrefix
$AttachmentSeq++
$AttachmentDataObj = "" | Select "Attachment Seq", "JDE Company", "Vendor Number", "Vendor Invoice Number", "Attachment Name", "Attachment System Name", "Job-run Date/Time Stamp"
Write-Host $Attachment.FileName
$file = Get-PnPFile -Url ($ItemAttachmentURLPrefix + $Attachment.FileName)
$bytes = (Get-PnPFile -Url ($ItemAttachmentURLPrefix + $Attachment.FileName)).OpenBinaryStream()
$name = "C:\Attachments\"+$Attachment.FileName
$fs = New-Object System.IO.StreamWriter($name, "OpenOrCreate")
$fs.Write($bytes, 0 , $bytes.Length)
$fs.Close()
$stream = $streamResult.Value
Write-Host $file.Name
Write-Host $stream.Name
}
}
but the files that will get saved can not be opened, for example this excel sheet will raise this error:-
while a pdf will raise this error:-
any advice what is wrong with my script?
Thanks
Use PnP PowerShell code in below format to download list item attachments to local folder:
Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/dev -Interactive
$listitem = Get-PnPListItem -List Employee -Id 2
$attachments = ForEach-Object {Get-PnPProperty -ClientObject $listitem -Property "AttachmentFiles"}
$attachments | ForEach-Object { Get-PnPFile -Url $_.ServerRelativeUrl -FileName $_.FileName -Path "E:\SharePoint\Documents" -AsFile }
Source: Read items attachments and write them to local folder

I want to use the ”For each” function in this powershell script to remove user profile from SharePoint 2016. Use of "For each function"

I have created a powershell script to remove user profile from SharePoint 2016. However, I need the script to collect data from .CSV where all the user Samaccount name and principal are stored. If the user exists then to remove the user profile. below is my script. I want to use the ”For each” function in this script. Could any expert help please?
#Import
$DEACTIVATED_AD_USERS_CSV = "D:\Te……Deactivated_Users_WDRmg_208.csv"
$SiteURL = “ (https:...)..”
$deactUsers = Import-Csv $DEACTIVATED_AD_USERS_CSV -Delimiter "`t"
$AccountName= "…"
#Get Objects
$ServiceContext = Get-SPServiceContext -site $SiteURL
$UserProfileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext)
#Get the User Profile
$UserProfile = $UserProfileManager.GetUserProfile($AccountName)
#remove user profile
$UserProfileManager.RemoveUserProfile($AccountName);
Assign the value from the samaccountname column of each row to the $AccountName variable:
#Import
$DEACTIVATED_AD_USERS_CSV = "D:\Te……Deactivated_Users_WDRmg_208.csv"
$SiteURL = "https://team.wdrmg.de/..."
$deactUsers = Import-Csv $DEACTIVATED_AD_USERS_CSV -Delimiter "`t"
foreach($user in $deactUsers){
# Grab the account name from the csv
$AccountName= $user.samaccountname
#Get Objects
$ServiceContext = Get-SPServiceContext -site $SiteURL
$UserProfileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext)
#Get the User Profile
$UserProfile = $UserProfileManager.GetUserProfile($AccountName)
#remove user profile
$UserProfileManager.RemoveUserProfile($AccountName)
}

How to use PowerShell to download files from SharePoint?

I've used the following sites to help me get this far and to troubleshoot.
Download file from SharePoint
How to download newest file from SharePoint using PowerShell
Mike Smith's Tech Training Notes SharePoint, PowerShell and .Net!
Upload file to a SharePoint doc library via PowerShell
Download latest file from SharePoint Document Library
How to iterate each folders in each of the SharePoint websites using PowerShell
PowerShell's Get-ChildItem on SharePoint Library
I am trying to download random files from SharePoint folder, and I have it working for when I actually know the file name and extension.
Working code with name and extension:
$SharePoint = "https://Share.MyCompany.com/MyCustomer/WorkLoad.docx"
$Path = "$ScriptPath\$($CustomerName)_WorkLoad.docx"
#Get User Information
$user = Read-Host "Enter your username"
$username = "$user#MyCompany"
$password = Read-Host "Enter your password" -AsSecureString
#Download Files
$WebClient = New-Object System.Net.WebClient
$WebClient.Credentials = New-Object System.Net.Networkcredential($UserName, $Password)
$WebClient.DownloadFile($SharePoint, $Path)
However, I don't seem to be able to figure out how to do it with multiple files of unknown names or extensions.
I have tried mapping a drive, only to end up with "drive mapping failed" & "The network path was not found." errors:
$SharePoint = Read-Host 'Enter the full path to Delivery Site'
$LocalDrive = 'P:'
$Credentials = Get-Credential
if (!(Test-Path $LocalDrive -PathType Container)) {
$retrycount = 0; $completed = $false
while (-not $completed) {
Try {
if (!(Test-Path $LocalDrive -PathType Container)) {
(New-Object -ComObject WScript.Network).MapNetworkDrive($LocalDrive,$SharePoint,$false,$($Credentials.username),$($Credentials.GetNetworkCredential().password))
}
$Completed = $true
}
Catch {
if ($retrycount -ge '5') {
Write-Verbose "Mapping SharePoint drive failed the maximum number of times"
throw "SharePoint drive mapping failed for '$($SharePoint)': $($Global:Error[0].Exception.Message)"
} else {
Write-Verbose "Mapping SharePoint drive failed, retrying in 5 seconds."
Start-Sleep '5'
$retrycount++
}
}
}
}
I've also used the following code with similar results or no results at all.
#Get User Information
$user = Read-Host "Enter your username"
$username = "$user#MyCompany"
$password = Read-Host "Enter your password" -AsSecureString
#Gathering the location of the Card Formats and Destination folder
$Customer = "$SharePoint\MyCustomer"
$Products = "$Path\$($CustomerName)\Products\"
#Get Documents from SharePoint
$credential = New-Object System.Management.Automation.PSCredential($UserName, $Password)
New-PSDrive -Credential $credential -Name "A" -PSProvider "FileSystem" -Root "$SharePoint"
net use $spPath #$password /USER:$user#corporate
#Get PMDeliverables file objects recursively
Get-ChildItem -Path "$Customer" | Where-Object { $_.name -like 'MS*' } | Copy-Item -Destination $Products -Force -Verbose
Without defined "input parameters", it's not exactly clear the full solution you need so I'll provide a few snippets of PowerShell that should be of use based on what you've described.
I'll spare you the basics of the various OOTB functions (i.e. Get-SPWeb, etc) though can provide those details as well if needed. I've also been overly explicit in the scripting, though know some of these lines could be chained, piped, etc to be made shorter & more efficient.
This example will iterate over the contents of a SharePoint Library and download them to your local machine:
$Destination = "C:\YourDestinationFolder\ForFilesFromSP"
$Web = Get-SPWeb "https://YourServerRoot/sites/YourSiteCollection/YourSPWebURL"
$DocLib = $Web.Lists["Your Doc Library Name"]
$DocLibItems = $DocLib.Items
foreach ($DocLibItem in $DocLibItems) {
if($DocLibItem.Url -Like "*.docx") {
$File = $Web.GetFile($DocLibItem.Url)
$Binary = $File.OpenBinary()
$Stream = New-Object System.IO.FileStream($Destination + "\" + $File.Name), Create
$Writer = New-Object System.IO.BinaryWriter($Stream)
$Writer.write($Binary)
$Writer.Close()
}
}
This is pretty basic; the variables up top are where on your local machine you wish to store the download files ($Destination), the URL of your SharePoint Site/Web ($Web) and the name of the Document Library (Your Doc Library Name).
The script then iterates through the items in the Library (foreach ($DocLibItem in $DocLibItems) {}), optionally filters for say items with a .docx file extension and downloads each to your local machine.
You could customize this further by targeting a specific sub-folder within the Doc Library, filter by metadata or properties of the Docs or even iterate over multiple Sites, Webs and/or Libraries in one script, optionally filtering those based on similar properties.

Changing User Profile Property for all the users in sharepoint 2010 using powershell

I want to change the edit setting and Display setting value of a user profile property called Department for all users. Can someone please tell me how to do it.
I can get to the department property with this powershell. Right now this Property Edit Setting is DO not Allow Users to edit this property and I want to make it Editable for every user.
Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue
$mySiteUrl = "http://www.test.com/mysite"
$site = Get-SPSite $mySiteUrl
$context = Get-SPServiceContext $site
$profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
$userProfile = $profileManager.GetUserProfile("Test\822");
$userProfile.Properties | sort DisplayName | FT DisplayName,Name,#{Label="Type";Expression={$_.CoreProperty.Type}}
$userProfile["Department"].Value
$site.Dispose()
Thanks
I guess you mean this property, right?
The following script demonstrates how to set IsUserEditable sub property of Departments user profile property:
if ((Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null)
{
Add-PsSnapin Microsoft.SharePoint.PowerShell
}
$siteUrl = "http://contoso.intranet.com/"
$site = Get-SPSite $siteUrl
$context = Get-SPServiceContext($site)
$profileConfigMgr = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($context)
$profilePropMgr = $profileConfigMgr.ProfilePropertyManager
$subtypePropMgr = $profilePropMgr.GetProfileSubtypeProperties("UserProfile")
$subtypeProp = $subtypePropMgr.GetPropertyByName("Department")
$subtypeProp.IsUserEditable = $true
$subtypeProp.Commit()
References
How to: Work with user profiles and organization profiles by using the server object model in SharePoint 2013

EWS Powershell Exchange 2013 FindFolders returns 0 results

I am currently trying to make this script run on exchange 2013 to convert folder types from IPF.IMAP to IPF.NOTE as the folders are not showing on mobile devices after being imported from Imap. This script returns 0 results after running and multiple Doesnt Exist. If I output the folder names they are coming through, so i am not sure why the FindFolders is not returning any results.
I tried turning on impersonation (commented out here) but get an error saying I do not have permissions to impersonate even though I am logged in as administrator and running on powershell as admin. I am not sure if this is even necessary as the script works fine and returns the folder names for both $mbxfolder.Name and $SfSearchFilter, but only until it hits the FindFolders line, then the TotalCount is always 0.
Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"
$exchService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
$exchService.UseDefaultCredentials = $true
$exchService.AutodiscoverUrl('email#domain.com', {$true})
$MBXID = "email#domain.com" #Define mailboxID
foreach ($MailboxIdentity in $MBXID) {
Write-Host "Searching for $MailboxIdentity"
$mailbox = (Get-Mailbox -Identity $MailboxIdentity)
$MailboxName = (Get-Mailbox -Identity $MailboxIdentity).PrimarySmtpAddress.ToString()
$MailboxRootid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName) #MsgFolderRoot selection and creation of new root
$MailboxRoot = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchService,$MailboxRootid)
#$exchService.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$MailboxName #Define impersonation
$folderid = $MailboxRootid
$f1 = $MailboxRoot
$fold = get-mailboxfolderstatistics $MailboxIdentity #Getting complete list of selected mailbox
foreach ($mbxfolder in $fold){
#Define Folder View Really only want to return one object
$fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(100) #page size for displayed folders
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep; #Search traversal selection Deep = recursively
#Define a Search folder that is going to do a search based on the DisplayName of the folder
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::Displayname,$MBXFolder.name) #for each folder in mailbox define search
$findFolderResults = $MailboxRoot.FindFolders($SfSearchFilter,$fvFolderView) #for each folder in mailbox define folder view (this is online task for store.exe) and perform search
if ($findFolderResults.TotalCount -eq 0){ "Folder Doesn't Exist" } #Info if folder still exist
else {"Folder Exist"
ForEach ($Folder in $findFolderResults.Folders) { #for each folder in folder results perform check of folder class
$folder.folderclass #Info about folder class
if ($Folder.folderclass -eq "IPF.Imap"){ #If folder class is target type, do change and update
$Folder.folderclass = "IPF.Note" #Folder class change in variable
Write-Host "Updating folder $folder.name to correct type IPF.Note. Folder will start to be visible in OWA"
$Folder.update() #Folder class update in mailbox via EWS
}
}
}
}
}
It doesn't really make much sense to enumerate the the folders using Get-MailboxFolderStatistics and then search for each folder in EWS. This is going to really slow and unnecessary (you have the folderId anyway from Get-MailboxFolderStatistics so you can just convert that and bind to it). However I would
Get rid of Get-MailboxFolderStatistics altogether and just use straight EWS to enumerate the Folders in the Mailbox and do your fixes as this will be much quicker eg
## Get the Mailbox to Access from the 1st commandline argument
$MailboxName = $args[0]
## Load Managed API dll
###CHECK FOR EWS MANAGED API, IF PRESENT IMPORT THE HIGHEST VERSION EWS DLL, ELSE EXIT
$EWSDLL = (($(Get-ItemProperty -ErrorAction SilentlyContinue -Path Registry::$(Get-ChildItem -ErrorAction SilentlyContinue -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Web Services'|Sort-Object Name -Descending| Select-Object -First 1 -ExpandProperty Name)).'Install Directory') + "Microsoft.Exchange.WebServices.dll")
if (Test-Path $EWSDLL)
{
Import-Module $EWSDLL
}
else
{
"$(get-date -format yyyyMMddHHmmss):"
"This script requires the EWS Managed API 1.2 or later."
"Please download and install the current version of the EWS Managed API from"
"http://go.microsoft.com/fwlink/?LinkId=255472"
""
"Exiting Script."
exit
}
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#$service.TraceEnabled = $true
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
#Define Function to convert String to FolderPath
function ConvertToString($ipInputString){
$Val1Text = ""
for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){
$Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))
$clInt++
}
return $Val1Text
}
#Define Extended properties
$PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
$folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
#Define the FolderView used for Export should not be any larger then 1000 folders due to throttling
$fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
#Deep Transval will ensure all folders in the search path are returned
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;
$psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
#Add Properties to the Property Set
$psPropertySet.Add($PR_Folder_Path);
$fvFolderView.PropertySet = $psPropertySet;
#The Search filter will exclude any Search Folders
$sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")
$fiResult = $null
#The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox
do {
$fiResult = $Service.FindFolders($folderidcnt,$sfSearchFilter,$fvFolderView)
foreach($ffFolder in $fiResult.Folders){
$foldpathval = $null
#Try to get the FolderPath Value and then covert it to a usable String
if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))
{
$binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)
$hexArr = $binarry | ForEach-Object { $_.ToString("X2") }
$hexString = $hexArr -join ''
$hexString = $hexString.Replace("FEFF", "5C00")
$fpath = ConvertToString($hexString)
}
"FolderPath : " + $fpath
"Folder Class : " + $ffFolder.FolderClass
}
$fvFolderView.Offset += $fiResult.Folders.Count
}while($fiResult.MoreAvailable -eq $true)
Cheers
Glen