migrate document with its meta data from one document library to a folder - sharepoint

I am beginner in sharepoint specially in powershell.
I am trying to move all the documents of a document library from one site to another site inside a folder. but I could just copy the files not metadata whcih come from contenttypes!
$PSSnapin = Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue Out-Null
clear
$org = "http://'YYYYY'/sites/XXXX"
$dest = "http://'YYYYY'/sites/XXXX/Subsite"
$orgLibrary = (Get-SPWeb $org).Folders["newdoc1"]
$destLibrary = (Get-SPWeb $dest).Folders["newdoc2"].SubFolders["folder1"]
$destFiles = $destLibrary.Files
foreach ($file in $orgLibrary.Files)
{
$curFile = $file.OpenBinary()
$destURL = $destFiles.Folder.Url + "/" + $file.Name
$destFiles.Add($destURL, $curFile, $true)
}

This one here does just that. Except for modified and created dates, all other fields are brought over.
$web = Get-SPWeb "http://sharepointed.com/"
$list = $web.Lists["Shared Documents"]
$spQuery = New-Object Microsoft.SharePoint.SPQuery
$spQuery.ViewAttributes = "Scope='Recursive'";
$spQuery.RowLimit = 2000
$caml = '<Where><Lt><FieldRef Name="Created" /><Value IncludeTimeValue="TRUE" Type="DateTime">2014-01-01T04:06:45Z</Value></Lt></Where> '
$spQuery.Query = $caml
do
{
$listItems = $list.GetItems($spQuery)
$spQuery.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
$listTotal = $listItems.Count
for ($x=$listTotal-1;$x -ge 0; $x--)
{
try
{
$listItems[$x].CopyTo("http://sharepoint/Docs/Documents/"+ $listItems[$x].name)
Write-Host("DELETED: " + $listItems[$x].name)
$listItems[$x].Recycle()
}
catch
{
Write-Host $_.Exception.ToString()
}
}
}
while ($spQuery.ListItemCollectionPosition -ne $null)
via Ian

Related

Running a powershell script using multiple threads

I'm trying to execute a powershell script from within another powershell script. My current script runs fine, but I'd like to try and speed it up if possible.
What my script does is import a list of contacts into Each users Contacts folder through EWS.
In my powershell script the script that handles the importing I call it like this (ImportContacts is a Function without any arguments):
. $rootPath\ImportContacts\ImportContacts.ps1
ImportContacts
When I run it normally, as I mentioned above everything works fine, I just would like to speed it up. I tried following some examples of implementing runspacepool in Powershell to take advantage of using multiple threads, but it doesn't seem to be working properly for me. I'm sure it's a silly syntax error, but I have this currently:
Measure-Command{
$MaxThreads = 5
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
$RunspacePool.Open()
$Jobs = #()
ImportContacts | Foreach-Object {
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.AddScript({ImportContacts})
$Jobs += $PowerShell.BeginInvoke()
}
while ($Jobs.IsCompleted -contains $false)
{
Start-Sleep 1
}}
This seems to do the job, but I can't tell a difference with the speed.
EDIT 5-15-21
To better assist with the question, here is how I retrieve the user data. I have a file I call called "ExportedContacts.ps1"
It's called like so:
$Users = & $rootPath\ExportContacts\ExportContacts.ps1
The contents of the file is this
$Users = Get-ADUser -Filter * -Properties extensionAttribute2, middlename, mobile, OfficePhone, GivenName, Surname, DisplayName, EmailAddress, Title, Company, Department, thumbnailPhoto | Where-Object {($_.extensionAttribute2 -like "DynamicDistro") -AND (($_.Mobile -ne $NULL) -OR ($_.OfficePhone -ne $NULL))} | Select-Object #{Name="First Name";Expression={$_.GivenName}},#{Name="Last Name";Expression={$_.Surname}},#{Name="Display Name";Expression={$_.DisplayName}},#{Name="Job Title";Expression={$_.Title}},#{Name="Company";Expression={$_.Company}},#{Name="Department";Expression={$_.Department}},#{Name="Mobile Phone";Expression={$_.Mobile}},#{Name="Home Phone";Expression={$_.OfficePhone}}, #{Name="Middle Name";Expression={$_.MiddleName}}, #{Name="E-mail Address";Expression={$_.EmailAddress}}, thumbnailPhoto | Sort-Object "Last Name"
return $Users
Then I import those contacts, similarly to how I mentioned above. The content of the import is as follows:
Function ImportContacts
{
Write-Host "Importing Contacts. This can take several minutes."
#******************************************************************
foreach ($ContactItem in $Users)
{
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$ExchangeContact = New-Object Microsoft.Exchange.WebServices.Data.Contact($service);
$ExchangeContact.NickName = ('{0} {1}' -f $ContactItem."First Name", $ContactItem."Last Name"). Trim()
$ExchangeContact.DisplayName = $ExchangeContact.NickName;
$ExchangeContact.FileAs = $ExchangeContact.NickName;
$ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1] = $ContactItem."E-mail Address";
$ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::HomePhone] = $ContactItem."Home Phone";
$ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::MobilePhone] = $ContactItem."Mobile Phone";
$ExchangeContact.Department = $ContactItem."Department";
$ExchangeContact.CompanyName = $ContactItem."Company";
$ExchangeContact.JobTitle = $ContactItem."Job Title";
$ExchangeContact.MiddleName = $ContactItem."Middle Name";
# Save the contact
$ExchangeContact.Save($ContactsFolder.Id);
}
}
I am also including the File that creates the contacts folder I specify and also deletes existing contacts (if the folder already exists), so that each import is a clean and updated import. I'd be curious if there is a faster way to clean the existing items?
Function CreateContactsFolder
{
Write-Host "Creating Contacts Folder and Cleaning up stale items. This can take a couple minutes."
Try
{
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$RootFolder.Load()
#Check to see if they have a contacts folder that we want
$FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where-Object {$_.DisplayName -eq $FolderName}
if($ContactsFolderSearch)
{
$ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
#If folder exists, connect to it. Clear existing Contacts, and reupload new (UPDATED) Contact Info
Write-Host "Folder alreads exists. We will remove all contacts under this folder."
# Attempt to empty the target folder up to 10 times.
$tries = 0
$max_tries = 0
while ($tries -lt 2)
{
try
{
$tries++
$ErrorActionPreference='Stop'
$ContactsFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true)
$tries++
}
catch
{
$ErrorActionPreference='SilentlyContinue'
$rnd = Get-Random -Minimum 1 -Maximum 10
Start-Sleep -Seconds $rnd
$tries = $tries - 1
$max_tries++
if ($max_tries -gt 100)
{
Write-Host "Error; Cannot empty the target folder; `t$EmailAddress"
}
}
}
}
else
{
#Contact Folder doesn't exist. Let's create it
try
{
Write-Host "Creating new Contacts Folder called $FolderName"
$ContactsFolder = New-Object Microsoft.Exchange.WebServices.Data.ContactsFolder($service);
$ContactsFolder.DisplayName = $FolderName
$ContactsFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
}
catch
{
Write-Host "Error; Cannot create the target folder; `t$EmailAddress"
}
}
return $ContactsFolder
}
Catch
{
Write-Host "Couldn't connect to the user's mailbox. Make sure the admin account you're using to connect to has App Impersonization permissions"
Write-Host "Check this link for more info: https://help.bittitan.com/hc/en-us/articles/115008098447-The-account-does-not-have-permission-to-impersonate-the-requested-user"
}
}

How to combine two powershell scripts for AD Reporting with Foreign Security Principals

I'm a DBA tasked with querying info from AD for a security audit. I need to create a Report that can put all AD objects into a CSV and resolve the Foreign Security Principals from another domain. I do have a trust between the domains. I have following script working in powershell coming from: http://social.technet.microsoft.com/Forums/scriptcenter/en-US/59d99252-2b1c-490e-818c-d1a645332293/powershell-ad-group-membership?forum=ITCG&prof=required
$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
$Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName
# Specify attributes to retrieve.
$Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
$Searcher.PropertiesToLoad.Add("sAMAccountName") > $Null
# Hash table.
$MemberList = #{}
# Retrieve all users, groups, and computers.
$Searcher.Filter = "(|(objectCategory=user)(objectCategory=group)(objectCategory=computer))"
$Results = $Searcher.FindAll()
ForEach ($Result In $Results)
{
$DN = $Result.Properties.Item("distinguishedName")
$Name = $Result.Properties.Item("sAMAccountName")
$MemberList.Add($($DN), $($Name))
}
# Filter on all groups.
$Searcher.Filter = "(objectCategory=group)"
$Searcher.PropertiesToLoad.Add("member") > $Null
$Results = $Searcher.FindAll()
ForEach ($Result In $Results)
{
$Name = $Result.Properties.Item("sAMAccountName")
$Line = """DN,$Name"""
$Members = $Result.Properties.Item("member")
ForEach ($Member In $Members)
{
If ($MemberList.ContainsKey($Member))
{
# Substitute the sAMAccountName from hash table.
$Line = $Line + ",""" + $MemberList[$Member] + """"
}
Else
{
# Use the Distinguished Name.
$Line = $Line + ",""" + $Member + """"
}
}
$Line
}
Then I found an script coming from http://activelydirect.blogspot.com/2011/01/dealing-with-foreignsecurityprincipal.html#comment-form , My hope is I could add the following to the first script and get them to resolve. But any input is welcome.
$securityPrincipalObject = New-Object System.Security.Principal.SecurityIdentifier($object.cn)
($domain, $sAMAccountName) = ($securityPrincipalObject.Translate([System.Security.Principal.NTAccount]).value).Split("\")
If your question is will the second script work for translating a Foreign Security Principle from a trusted domain into a username, then yes, I have used similar code to do so with good results. Just be sure that what you pass to the System.Security.Principal.SecurityIdentifier constructor is only the SID of the FSP (e.g. "S-1-5-21-...") and not the distinguishedName or "CN=S-1-5-21-...".
UPDATE:
if ($dN.StartsWith("CN=S-"))
{
$SIDText = ($dN.Split(","))[0].SubString(3)
$SID = New-Object System.Security.Principal.SecurityIdentifier $SIDText
Write-Output $SID.Translate([System.Security.Principal.NTAccount]).Value
}
else
{
Write-Output $dN
}

PowerShell script to extract .xls file from specific Outlook folder

I want to extract and save an .xls file from an email I receive daily. I have a rule set up which saves the email in an Outlook mailbox, within a specific subfolder of the Inbox.
The Outlook folder structure looks like this:
-> Inbox
--> Data (subfolder of "Inbox")
---> ToExtract (subfolder of "Data")
I need to extract the .xls file from the "ToExtract" folder.
I found a script that does most of the work for me, but it requires the user to supervise the script and manually select which Outlook folder to search. I need to change the script so it just points to the "ToExtract" subfolder.
The code is below. It works fine, but I need to modify the pickfolder() part.
#file path
$filepath = “c:\test\”
#set outlook to open
$o = New-Object -comobject outlook.application
$n = $o.GetNamespace(“MAPI”)
#you'll get a popup in outlook at this point where you pick the folder you want to scan
$f = $n.pickfolder()
#date string to search for in attachment name
$date = Get-Date -Format yyyyMMdd
#now loop through them and grab the attachments
$f.Items | foreach {
$_.attachments | foreach {
Write-Host $_.filename
$a = $_.filename
If ($a.Contains($date)) {
$_.saveasfile((Join-Path $filepath $a))
}
}
}
Give this a shot.
$Account = $n.Folders | ? { $_.Name -eq 'username#domain.com' };
$Inbox = $Account.Folders | ? { $_.Name -match 'Inbox' };
$f = $Inbox.Folders | ? { $_.Name -match 'ToExtract' };
$MailboxName = "MAILBOX"
$Subject = "EMAIL SUBJECT"
$ProcessedFolderPath = "/Inbox/Processed"
$downloadDirectory = "c:\temp"
Function FindTargetFolder($FolderPath){
$tfTargetidRoot = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$tfTargetidRoot)
$pfArray = $FolderPath.Split("/")
for ($lint = 1; $lint -lt $pfArray.Length; $lint++) {
$pfArray[$lint]
$fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+isEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$pfArray[$lint])
$findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)
if ($findFolderResults.TotalCount -gt 0){
foreach($folder in $findFolderResults.Folders){
$tfTargetFolder = $folder
}
}
else{
"Error Folder Not Found"
$tfTargetFolder = $null
break
}
}
$Global:findFolder = $tfTargetFolder
}
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)
$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$sidbind = "LDAP://<SID=" + $windowsIdentity.user.Value.ToString() + ">"
$aceuser = [ADSI]$sidbind
$service.AutodiscoverUrl($aceuser.mail.ToString())
FindTargetFolder($ProcessedFolderPath)
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
$Sfir = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $false)
$Sfsub = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject, $Subject)
$Sfha = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::HasAttachments, $true)
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
$sfCollection.add($Sfir)
$sfCollection.add($Sfsub)
$sfCollection.add($Sfha)
$view = new-object Microsoft.Exchange.WebServices.Data.ItemView(2000)
$frFolderResult = $InboxFolder.FindItems($sfCollection,$view)
foreach ($miMailItems in $frFolderResult.Items){
$miMailItems.Subject
$miMailItems.Load()
foreach($attach in $miMailItems.Attachments){
$attach.Load()
$fiFile = new-object System.IO.FileStream(($downloadDirectory + “\” + $attach.Name.ToString()), [System.IO.FileMode]::Create)
$fiFile.Write($attach.Content, 0, $attach.Content.Length)
$fiFile.Close()
write-host "Downloaded Attachment : " + (($downloadDirectory + “\” + $attach.Name.ToString()))
}
$miMailItems.isread = $true
$miMailItems.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
[VOID]$miMailItems.Move($Global:findFolder.Id)
}
From https://gist.github.com/bleep-io/5151579

How to update SharePoint enterprise keywords with multiple values using powershell?

I'm trying to write a powershell script that looks at a user defined variable and updates the enterprise keywords for each item in a list that matches that value.
For instance, say you have a page in SP that has the managed metadata keywords: new, fresh, clean
I want a script that asks a user what keyword they want to swap out. So a user would specify a variable as: fresh and another variable as: fresher and it would update any item with the keyword fresh to fresher.
Here's what I 've used before but doesn't work now because there are mutliple values:
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue
$webURL = <MY SP URL>
$listName = <MY LIST NAME>
$web = Get-SPWeb $webURL
$list = $web.Lists[$listName]
$listitems = $list.items
$session = Get-SPTaxonomySession -Site $web.Site
$termStore = $session.TermStores["Managed Metadata Service"]
$group = $termStore.Groups["Resources"]
$termset = $group.TermSets["Wiki Categories"]
$terms = $termSet.GetTerms(100)
$wiki1 = read-host "Enter the wiki category you want to update"
$wiki2 = read-host "Enter the replacement wiki category"
$term = $terms | ?{$_.name -eq $wiki2}
Foreach($item in $listitems)
{$wiki = $item["Wiki Categories"]
if($wiki.label -eq $term)
{
$spitem = [Microsoft.SharePoint.SPListItem]$item;
$taxfield = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$spitem.Fields["Wiki Categories"]
$taxfield.SetFieldValue($spitem, $term)
$spitem.Update()
$spitem.File.Publish("True")
}
}
I'm pretty sure the issue is with this line:
$term = $terms | ?{$_.name -eq $wiki2}
And this line:
$taxfield.SetFieldValue($spitem, $term)
The problem is that you passing a TermCollection ($term) to SetFieldValue where you should pass in a TaxonomyFieldValueCollection.
You can convert them like this:
$taxfield = $spitem.Fields["Wiki Categories"]
$tfvc = new-object -typename Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection -argumentlist $taxfield;
foreach($t in $ term)
{
$tfv = new-object -typename Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue -argumentlist $taxfield
$tfv.TermGuid = $t.Id
$tfv.Label = $t.Name
$tfvc.Add($tfv)
}
...
$taxfield.SetFieldValue($spitem, $tfvc)

SharePoint 2010, Powershell - Loop through all Document Libraries, create view and set it as default

I'm having a little trouble with a script I'm working on for SharePoint 2010. I'm only a beginner with Powershell so haven't been able to to spot the no doubt obvious and glaring problem.
The desire for the script is to loop through each web-site in each site collection, create a view for Document Libraries only and set that view as default.
Issue 1) Currently it appears to enumerate the Document Libraries but then creates the view multiple times in the first library it found. Something is wrong with the foreach loops but I don't know what.
Issue 2) I need to integrate this section so I can set the view as default but I'm not too sure where to stick it so it loops through with the changes to each library.
$site= New-Object Microsoft.SharePoint.SPSite $siteURL
$web=$site.OpenWeb()
$list=$web.Lists["$list"]
$view=$list.Views["Detailed"]
$view.DefaultView = $true
$view.Update()
$list.Update()
Any assistance on these two issues would be of great help:-). Thanks, Ashley
Full Script:
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
$siteURL = "http://sp14fdev01/"
$site = Get-SPSite($siteURL)
foreach($web in $site.AllWebs) {
foreach($list in $web.Lists) {
if($list.BaseType -eq "DocumentLibrary") {
$site = New-Object Microsoft.SharePoint.SPSite($SiteURL) ;
$web = $site.OpenWeb($SiteURL);
$list = $web.Lists.item($listname);
$viewfields = New-Object System.Collections.Specialized.StringCollection
$viewfields.Add("DocIcon")
$viewfields.Add("LinkFilename")
$viewfields.Add("_UIVersionString")
$viewfields.Add("FileSizeDisplay")
$viewfields.Add("Created")
$viewfields.Add("Modified")
$viewfields.Add("Editor")
[void]$list.Views.Add("Detailed", $viewfields, "", 100, $true, $true)
$list.Update();
}}
$web.Dispose();
$site.Dispose();
}
The changes are explained in the comments.
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
$siteURL = "http://sp14fdev01/"
$site = Get-SPSite($siteURL)
foreach($web in $site.AllWebs) {
foreach($list in $web.Lists) {
if($list.BaseType -eq "DocumentLibrary") {
// the variables `$web` and `$list` already reference the objects you need
//$site = New-Object Microsoft.SharePoint.SPSite($SiteURL) ;
//$web = $site.OpenWeb($SiteURL);
// new instance of the list is necessary to avoid the error "Collection was modified"
$newList = $web.Lists.item($list.ID);
$viewfields = New-Object System.Collections.Specialized.StringCollection
$viewfields.Add("DocIcon")
$viewfields.Add("LinkFilename")
$viewfields.Add("_UIVersionString")
$viewfields.Add("FileSizeDisplay")
$viewfields.Add("Created")
$viewfields.Add("Modified")
$viewfields.Add("Editor")
[void]$newList.Views.Add("Detailed", $viewfields, "", 100, $true, $true)
$newList.Update();
// setting the default view
$view=$newList.Views["Detailed"]
$view.DefaultView = $true
$view.Update()
}
}
$web.Dispose();
}
$site.Dispose();

Resources