Monday, January 15, 2018

SharePoint Online: Export List Version History to Excel using PowerShell

Requirement: Export all versions of SharePoint Online List Items to CSV (Excel) file.
sharepoint online export list version history to excel using powershell
PowerShell to Extract and Export List Item's Version History to CSV File:
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

Function Export-VersionHistory()
{
  param
    (
        [Parameter(Mandatory=$true)] [string] $SiteURL,
        [Parameter(Mandatory=$true)] [string] $ListName,
        [Parameter(Mandatory=$true)] [string] $CSVFile
    )
    Try {

        #Delete the Output report file if exists
        if (Test-Path $CSVFile) { Remove-Item $CSVFile }

        #Get Credentials to connect
        $Cred= Get-Credential
        $Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

        #Setup the context
        $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
        $Ctx.Credentials = $Credentials
        
        #Get the List
        $List = $Ctx.Web.Lists.GetByTitle($ListName)
        $Ctx.Load($List)
        $Ctx.ExecuteQuery()
        
        #Get all items
        $Query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery()
        $ListItems = $List.GetItems($Query)
        $Ctx.Load($ListItems)
        $Ctx.ExecuteQuery()

        #Array to hold result
        $VersionHistoryData = @()

        #Iterate throgh each item
        Foreach ($Item in $ListItems)
        {
            write-host "Processing Item:" $item.id -f Yellow
            
            #Get all versions of the list item
            $Versions = $Item.versions
            $ctx.Load($Versions)
            $Ctx.ExecuteQuery()

            If($Versions.count -gt 0)
            {
                #Iterate each version
                Foreach($Version in $Versions)
                {
                    #Get the Creator object of the version
                    $CreatedBy =  $Version.createdby
                    $Ctx.Load($CreatedBy)
                    $Ctx.ExecuteQuery()

                    #Send Data to object array
                    $VersionHistoryData += New-Object PSObject -Property @{
                    'Item ID' = $Item.ID
                    'Title' =  $Version.FieldValues["Title"]
                    'Version Label' = $Version.VersionLabel 
                    'Version ID' = ($Version.VersionId/512)
                    'Created On' = (Get-Date ($Version.Created) -Format "yyyy-MM-dd/HH:mm:ss")
                    'Created By' = $CreatedBy.Email
                    }
                }
            }
        }
        
        #Export the data to CSV
        $VersionHistoryData | Export-Csv $CSVFile -Append -NoTypeInformation

        write-host -f Green "Version History Exported Successfully to:" $CSVFile
     }
    Catch {
        write-host -f Red "Error Exporting version History to CSV!" $_.Exception.Message
    }
}

#Set parameter values
$SiteURL="https://crescent.sharepoint.com"
$ListName="Projects"
$CSVFile="C:\Temp\VersionHistory.csv"

#Call the function to generate version History Report
Export-VersionHistory -SiteURL $SiteURL -ListName $ListName -CSVFile $CSVFile

Important: This script works ONLY on CSOM version 16.1.6906.1200 or Later! Download the latest version of CSOM SDK from https://www.microsoft.com/en-us/download/confirmation.aspx?id=42038

This script produces a CSV file with version data such as:
  • Version Label,
  • Version ID
  • When the version was created
  • Who created the version
  • Title field value of the version (You can add any additional field too!)


You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


Thursday, January 4, 2018

SharePoint Online: Find and Delete Orphaned Users using PowerShell

find and delete orphaned users in sharepoint online

What is "Orphaned Users" in SharePoint Online?
In short, Orphaned users are those who deleted from the authentication provider (such as removed from Active Directory when user leaves the organization), and still continue to exist in SharePoint online sites! scanning each user in SharePoint online site collection for orphaned users could take days to complete! Since, Here is my PowerShell script to search for orphan users and delete them.

Pr-Requisites: Before using this script, you need to have SharePoint Online Management Shell (https://www.microsoft.com/en-us/download/details.aspx?id=35588) and Azure Active Directory Module (https://technet.microsoft.com/en-us/library/dn975125.aspx) installed on your machine!

Find Orphan Users in SharePoint Online using PowerShell:
This script scans each and every user from the given site collection URL and exports list of orphaned users to a CSV file.

#Import SharePoint Online and Azure Online modules
Import-Module Microsoft.Online.SharePoint.Powershell
Import-Module MSOnline

Function Generate-OrphanedUsersReport ()
{
param
    (
        [Parameter(Mandatory=$true)] [string] $AdminURL,
        [Parameter(Mandatory=$true)] [string] $SiteURL,
        [Parameter(Mandatory=$true)] [string] $ReportOutput        
    )
Try {
    #Get Credentials to connect
    $Cred = Get-Credential

    #Connect to SharePoint and Azure AD
    Connect-MsolService -Credential $cred
    Connect-SPOService -Url $AdminURL -Credential $Cred

    #Function to check if a user account exists
    Function Check-UserExists()
    {
        Param( [Parameter(Mandatory=$true)] [string]$UserID )
    
        $User=Get-Msoluser -UserPrincipalName $UserID -Erroraction SilentlyContinue
        if ($User -ne $null)
        {
            Return $True
        }
        else
        {
            Return $false
        }
    }
    $OrphanedUsers = @()

    #Get all users of a given SharePoint Online site collection
    $AllUsers = Get-SPOUser $SiteURL -Limit ALL

    Foreach($User in $AllUsers)
    {
        #Exclude Built-in User Accounts and Security Groups 
        if(($User.DisplayName.ToLower() -ne "nt authority\authenticated users") -and ($User.LoginName.ToLower() -ne "sharepoint\system") -and 
        ($User.DisplayName.ToLower() -ne "sharepoint app") -and ($user.IsGroup -eq $false ) -and(-not $user.DisplayName.ToLower().Contains("_spocache")) -and 
        (-not $user.DisplayName.ToLower().Contains("_spocrawl")) -and ($User.DisplayName.ToLower() -ne "sharepoint service administrator") -and 
        ($User.DisplayName.ToLower() -ne "guest contributor") -and ($User.DisplayName.ToLower() -ne "everyone except external users")-and ($User.DisplayName.ToLower() -ne "company administrator"))
        {
            Write-host "Checking user $($user.DisplayName)" -f Yellow
            #Check if user exists
            if((Check-UserExists $User.LoginName) -eq $False)
            {
                Write-Host "User Doesn't Exists: $($user.DisplayName) - $($User.LoginName)" -f Red

                #Send the Result to CSV 
                $Result = new-object PSObject
                $Result| add-member -membertype NoteProperty -name "LoginName" -Value $User.LoginName
                $Result | add-member -membertype NoteProperty -name "DisplayName" -Value $User.DisplayName
                $OrphanedUsers += $Result
            }
        }
    }
    #Export results to CSV
    $OrphanedUsers | Export-csv $ReportOutput -notypeinformation

        Write-host "Orphan Users Report Generated to $ReportOutput" -f Green
   }

    Catch {
    write-host -f Red "Error Deleting Unique Permissions!" $_.Exception.Message
    }
}

#Config Parameters
$AdminURL ="https://crescent-admin.sharepoint.com"
$SiteURL = "https://crescent.sharepoint.com"
$ReportOutput="C:\Temp\OrphanUsers.csv"

#Call the function to find and generate orphaned users report
Generate-OrphanedUsersReport -AdminURL $AdminURL -SiteURL $SiteURL -ReportOutput $ReportOutput
Be sure the CSV generated doesn't include any built-in user accounts and groups, prior providing the CSV file as an input to the next step of removing orphan users!

How to Delete Orphan Users from SharePoint Online with PowerShell: 
While its possible to remove each user from SharePoint online site collection individually, it becomes cumbersome when we have large number of orphan users to  remove! Here is the PowerShell script to read orphan users from the CSV file generated in previous step and remove them all in one go!
#Import SharePoint Online module
Import-Module Microsoft.Online.SharePoint.Powershell

Function Remove-OrphanedUsers ()
{
param
    (
        [Parameter(Mandatory=$true)] [string] $AdminURL,
        [Parameter(Mandatory=$true)] [string] $SiteURL,
        [Parameter(Mandatory=$true)] [string] $ReportInput        
    )
    Try {
        #Get Credentials to connect
        $Cred = Get-Credential
   
        #Connect to SharePoint online
        Connect-SPOService -Url $AdminURL -Credential $Cred

        #Get the Data from CSV and Add to SharePoint List
        $OrphanUsers = Import-Csv $ReportInput
        Foreach ($Row in $OrphanUsers) 
        {
            #Remove user from site
            Remove-SPOUser -Site $SiteURL -LoginName $Row.LoginName
            Write-host "Removed the Orphaned User $($Row.DisplayName) from $($SiteURL)"   
        }
            Write-host "Orphaned Users Removed from SharePoint Online Site!"
       }
    Catch {
    write-host -f Red "Error Deleting Unique Permissions!" $_.Exception.Message
    }
}

#Config Parameters
$AdminURL ="https://crescent-admin.sharepoint.com"
$SiteURL = "https://crescent.sharepoint.com"
$ReportInput="C:\Temp\OrphanUsers.csv"

#Call the function to Remove Orphaned users
Remove-OrphanedUsers -AdminURL $AdminURL -SiteURL $SiteURL -ReportInput $ReportInput
You can use these functions to find and/or remove orphaned users from all site collections. Just add:
Get-SPOSite -Limit all | ForEach-Object { 
  #Call the function to find and generate orphaned users report
  Generate-OrphanedUsersReport -AdminURL $AdminURL -SiteURL $_.Url -ReportOutput $ReportOutput
}


You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


Wednesday, December 20, 2017

SharePoint Online: Add Site Column to List using PowerShell

Requirement: Add an existing site column to SharePoint list or library using PowerShell.

How to Add a Site Column to SharePoint Online List?
Site columns in SharePoint provides great reusability without having to recreate same columns multiple times! Once created, we can utilize them in any number of lists and libraries. To add a site column to SharePoint list, follow these steps:

  • Go to List Settings >> Under Columns, Click on "Add from existing site columns" link.
  • From the available site columns, pick the required site column(s) and click on Add button.
    sharepoint online powershell to add site column to list
  • Click OK to save your changes.

Add Site Column to List or Library with PowerShell:
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
 
#Variables
$SiteURL="https://crescent.sharepoint.com"
$ListName="Projects"
$SiteColumnName="Department"

$Cred= Get-Credential
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

#Setup the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = $Credentials
$Web = $Ctx.web

#Get the list
$List=$Ctx.Web.Lists.GetByTitle($ListName)
    
#Get the Site column
$Field = $Web.Fields.GetByTitle($SiteColumnName)

#Add the site column to the list
$List.Fields.Add($Field)
$ctx.ExecuteQuery() 
    
Write-host "Site Column Added to the List Successfully!" -f Green
Lets add some error handling to the above code to handle scenarios, such as:
  • What if the given site column doesn't exist?
  • What if the given site column is already added to the list?
  • What if the given list doesn't exist? or what if the given credentials are invalid?, etc.
PowerShell to Add a Site Column to SharePoint List:
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
 
#Parameters
$SiteURL="https://crescent.sharepoint.com"
$ListName="Projects"
$SiteColumnName="Department"

Try {
    $Cred= Get-Credential
    $Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Credentials
    $Web = $Ctx.web

    #Get the Site column from web
    $SiteColumns = $Web.Fields
    $Ctx.Load($SiteColumns)
    $Ctx.ExecuteQuery()
    $SiteColumn = $SiteColumns | Where {$_.Title -eq $SiteColumnName}
    
    #Check if given site column exists
    if($SiteColumn -eq $Null)
    {
        Write-host "Site Column $SiteColumnName doesn't exists!" -f Yellow
    }
    else
    {
        #Get the list
        $List=$Ctx.Web.Lists.GetByTitle($ListName)

        #Check if the Filed exist in list already
        $Fields = $List.Fields
        $Ctx.Load($List)
        $Ctx.Load($Fields)
        $Ctx.ExecuteQuery() 
        $Field = $Fields | where {$_.Title -eq $SiteColumnName}
        if($Field -ne $NULL)  
        {
            Write-host "Column Name $SiteColumnName already exists in the list!" -f Yellow
        }
        else
        {
            #Add the site column to the list
            $NewColumn = $List.Fields.Add($SiteColumn)
            $ctx.ExecuteQuery() 
    
            Write-host "Site Column Added to the List Successfully!" -f Green
        }
    }
}
Catch {
    write-host -f Red "Error Adding Site Column to List!" $_.Exception.Message
}


You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


Wednesday, October 11, 2017

Create Search Service Application in SharePoint 2016 Multi-Server Farm using PowerShell

The Search Service Application in SharePoint provides search functionality, as its name implies. We have to use PowerShell scripts to configure search service application from SharePoint 2013 on wards as there is no user interface to configure the Search topology from SharePoint Central Admin - although we can create new search service application instance.

Configuring SharePoint 2016 Search Service Application for Multi-Server Farm:

While my earlier article addresses How to create search service application in SharePoint 2013 - Single Server farm, for Standalone SharePoint 2013 or SharePoint 2016 installations, In a production environment we may have to distribute and load balance search components to different servers in a multi-server farm.

Here is the proposed Farm search topology in high level:
create sharepoint 2016 search service application with Powershell

I've the below servers in my SharePoint 2016 environment using Shared MinRoles:
  • Web Front End + Distributed Cache Servers - 2
  • Application + Search Servers - 2
Since we'll be using multiple servers in SharePoint farm, we can add Redundancy, Performance, and Reliability  to the search application by splitting the six search components (Admin, Crawl, Content Processing, Indexing, Analytics, and Query Processing) among them. Also, we will configure two index partitions to ensure both servers have a replica of the search index.

PowerShell to Configure Search Service Application in SharePoint 2016:
Modifications to the search topology requires a decent understanding of SharePoint search topology as well as PowerShell. Here is the PowerShell script to create search service application in a multi-server SharePoint 2016 environment. You can add or modify the topology components according to your environment. Lets take an example. In my case we've two SharePoint Search App Servers to host all search components. Creating search service application for SharePoint 2016 involves these steps at high level:
  1. Creating Managed account, application pool for search service application.
  2. Creating SharePoint Search Service Application & Proxy
  3. Starting Search service instance on all servers participating search topology
  4. Creating an Search Topology and adding Search components and then Activating the search topology
Create Search Service Application using PowerShell:
Here is the PowerShell script to create SharePoint 2016 search service application.
Copy-Paste this script to PowerShell ISE or any other PowerShell editor (PowerGUI?), set the configuration parameters according to your environment and run one step at a time!

Create folders for Index Location & Replica Location (as in variables: $PrimaryIndexLocation & $ReplicaIndexLocation) on all servers which are hosting search topology index components, prior running the main script!
#Set the primary and replica index locations
$PrimaryIndexLocation = "D:\SearchIndex"
$ReplicaIndexLocation = "E:\SearchIndexReplica"

#Prepare Index Locations
Remove-Item -Recurse -Force -LiteralPath $PrimaryIndexLocation -ErrorAction SilentlyContinue
MKDIR -Path $PrimaryIndexLocation -Force
Remove-Item -Recurse -Force -LiteralPath $ReplicaIndexLocation -ErrorAction SilentlyContinue
MKDIR -Path $ReplicaIndexLocation -Force

PowerShell Script to Configure Search Service Application in SharePoint 2016:
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

#Region Config_Parameters
#Settings for Search Service Application
$SearchServiceApplicationName = "Search Service Application"
$SearchServiceApplicationProxyName = "Search Service Application Proxy"
$SearchAppPoolAccount = "Crescent\SP16_Services"
$SearchDatabaseServer = "SP16_SQL"
$SearchServiceApplicationDatabase = "Crescent_Search_ServiceApp"
$SearchAppPoolName = "Service Application App Pool"

#Farm Server topology: App with Search role: 2
$SearchAppServer1 = "SP16-APPSrv01"
$SearchAppServer2 = "SP16-APPSrv02"

#Set the primary and replica index locations
$PrimaryIndexLocation = "D:\SearchIndex"
$ReplicaIndexLocation = "E:\SearchIndexReplica"
#EndRegion


#*** Step 1: Create Managed Account, Service Application Pool for Search Service Application **** 
#Check if Managed account is registered already
Write-Host -ForegroundColor Yellow "Checking if the Managed Accounts already exists..."
$SearchAppPoolAccount = Get-SPManagedAccount -Identity $SearchAppPoolAccount -ErrorAction SilentlyContinue
If ($SearchAppPoolAccount -eq $null)
{
    Write-Host "Please Enter the password for the Service Account..."
    $AppPoolCredentials = Get-Credential $SearchAppPoolAccount
    $SearchAppPoolAccount = New-SPManagedAccount -Credential $AppPoolCredentials
    write-host "Managed Account has been Created!" -ForegroundColor Green
}

#Try to Get the existing Application Pool
Write-Host -ForegroundColor Yellow "Checking if the App Pool already exists..."
$SearchServiceAppPool = Get-SPServiceApplicationPool -Identity $SearchAppPoolName -ErrorAction SilentlyContinue
#If Application pool Doesn't exists, Create it
if ($SearchServiceAppPool -eq $Null)
{ 
    $SearchServiceAppPool = New-SPServiceApplicationPool -Name $SearchAppPoolName -Account $SearchAppPoolAccount 
    write-host "Created New Service Application Pool!" -ForegroundColor Green
}

#*** Step 2: Start Search Service Instances on required servers ***
Write-host "Starting Service Instances..." -ForegroundColor Green
#Search App Server 1
$SearchAppInstance1 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer1
if(-not($SearchAppInstance1.Status -eq "Online"))
{
    Write-Host -ForegroundColor Yellow "Starting Search Service instance on $SearchAppServer1..." -NoNewline
    $SearchAppInstance1 | Start-SPEnterpriseSearchServiceInstance
    while ($SearchAppInstance1.Status -ne "Online")
    {
        Write-Host -ForegroundColor Green "." -NoNewline
        Start-Sleep -Seconds 5
        $SearchAppInstance1 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer1
    }
    Write-Host -ForegroundColor Green "Started Search Service instance on $SearchAppServer1"
}
Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance $SearchAppServer1 -ErrorAction SilentlyContinue

#Search App Server 2
$SearchAppInstance2 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer2
if(-not($SearchAppInstance2.Status -eq "Online"))
{
    Write-Host -ForegroundColor Yellow "Starting Search Service instance on $SearchAppServer2..." -NoNewline
    $SearchAppInstance2 | Start-SPEnterpriseSearchServiceInstance
    while ($SearchAppInstance2.Status -ne "Online")
    {
        Write-Host -ForegroundColor Green "." -NoNewline
        Start-Sleep -Seconds 5
        $SearchAppInstance2 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer2
    }
    Write-Host -ForegroundColor Green "Started Search Service instance on $SearchAppServer2"
}
Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance $SearchAppServer2 -ErrorAction SilentlyContinue


#*** Step 3: Create Search Service Application and proxy**** 
Write-host "Creating Search Service Application..." -ForegroundColor Yellow
# Get the Search Service Application
$SearchServiceApplication = Get-SPEnterpriseSearchServiceApplication -Identity $SearchServiceApplicationName -ErrorAction SilentlyContinue
# Create the Search Service Application, If it doesn't exists! 
if(!$SearchServiceApplication)
{
    $SearchServiceApplication = New-SPEnterpriseSearchServiceApplication -Name $SearchServiceApplicationName -ApplicationPool $SearchServiceAppPool -DatabaseServer $SearchDatabaseServer -DatabaseName $SearchServiceApplicationDatabase
    write-host "Created New Search Service Application" -ForegroundColor Green
}

#Get the Search Service Application Proxy
$SearchServiceAppProxy = Get-SPEnterpriseSearchServiceApplicationProxy -Identity $SearchServiceApplicationProxyName -ErrorAction SilentlyContinue
# Create the Proxy, If it doesn't exists! 
if(!$SearchServiceAppProxy)
{
    $SearchServiceAppProxy = New-SPEnterpriseSearchServiceApplicationProxy -Name $SearchServiceApplicationProxyName -SearchApplication $SearchServiceApplication 
    write-host "Created New Search Service Application Proxy" -ForegroundColor Green
}

#*** Step 4: Create New Search Topology and add components to it
Write-Host -ForegroundColor Yellow "Creating Search Service Topology..."
# Create New Search Topology 
$SearchTopology =  New-SPEnterpriseSearchTopology -SearchApplication $SearchServiceApplication

#Admin Component:App 01 & 02
New-SPEnterpriseSearchAdminComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1
New-SPEnterpriseSearchAdminComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2

#Content Processing:App 01 & 02
New-SPEnterpriseSearchContentProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1
New-SPEnterpriseSearchContentProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2

#Analytics processing:App01 & 02
New-SPEnterpriseSearchAnalyticsProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1
New-SPEnterpriseSearchAnalyticsProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2

#Crawl components:App 01 & 02
New-SPEnterpriseSearchCrawlComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1
New-SPEnterpriseSearchCrawlComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2

#Query processing: App 01 & 02
New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1
New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2

#Create Index Components : App 01 & 02
#Two index partitions and replicas for each partition
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1 -RootDirectory $PrimaryIndexLocation -IndexPartition 0
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2 -RootDirectory $ReplicaIndexLocation -IndexPartition 0
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance2 -RootDirectory $PrimaryIndexLocation -IndexPartition 1
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance1 -RootDirectory $ReplicaIndexLocation -IndexPartition 1

#Activate the Toplogy for Search Service
$SearchTopology.Activate() # Or Use: Set-SPEnterpriseSearchTopology -Identity $SearchTopology

#Remove all inactive topologies
$InactiveTopologies = Get-SPEnterpriseSearchTopology -SearchApplication $SearchServiceApplication | Where {$_.State -ne "Active"}
$InactiveTopologies | Remove-SPEnterpriseSearchTopology -Confirm:$false

Write-Host -ForegroundColor Green "Search Service Application has been created. Please start a Full Crawl!"

Once everything is setup, You can see the Search topology visually from SharePoint Central Administration site.
sharepoint 2016 configure search service application powershell

Scale-out SharePoint 2016 Search to Multiple Servers:
The above script is for SharePoint farm with two "Application with Search" Server. What if your SharePoint farm has more than two app servers, say Four?
Best Practice: Assign Query and Index components together in the same server(s) and place remaining components to other Search App servers.
create sharepoint 2016 search service application
Also, in the script do below changes: Farm Topology Section, include two more servers: Say, App03 and App04
#Farm Server topology: App+Search: 4
$SearchAppServer1 = "Cre-SP16App01"
$SearchAppServer2 = "Cre-SP16App02"
$SearchAppServer3 = "Cre-SP16App03"
$SearchAppServer4 = "Cre-SP16App04"
Start Service instances on app servers 3 & 4 :
#Search App Server 3
$SearchAppInstance3 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer3
if(-not($SearchAppInstance3.Status -eq "Online"))
{
    Write-Host -ForegroundColor Yellow "Starting Search Service instance on $SearchAppServer3..." -NoNewline
    $SearchAppInstance3 | Start-SPEnterpriseSearchServiceInstance
    while ($SearchAppInstance3.Status -ne "Online")
    {
        Write-Host -ForegroundColor Green "." -NoNewline
        Start-Sleep -Seconds 5
        $SearchAppInstance3 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer3
    }
    Write-Host -ForegroundColor Green "Started Search Service instance on $SearchAppServer3"
}
Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance $SearchAppServer3 -ErrorAction SilentlyContinue
#Search App Server 4
$SearchAppInstance4 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer4
if(-not($SearchAppInstance4.Status -eq "Online"))
{
    Write-Host -ForegroundColor Yellow "Starting Search Service instance on $SearchAppServer4..." -NoNewline
    $SearchAppInstance4 | Start-SPEnterpriseSearchServiceInstance
    while ($SearchAppInstance4.Status -ne "Online")
    {
        Write-Host -ForegroundColor Green "." -NoNewline
        Start-Sleep -Seconds 5
        $SearchAppInstance4 = Get-SPEnterpriseSearchServiceInstance $SearchAppServer4
    }
    Write-Host -ForegroundColor Green "Started Search Service instance on $SearchAppServer4"
}
Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance $SearchAppServer4 -ErrorAction SilentlyContinue 
On creating Search topology: Assign Query and Index Roles to App Servers 3 and 4
#Query processing: App 03 & 04
New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance3
New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance4

#Create Index Components : App 03 & 04
#Two index partitions and replicas for each partition
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance3 -RootDirectory $PrimaryIndexLocation -IndexPartition 0
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance4 -RootDirectory $ReplicaIndexLocation -IndexPartition 0
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance4 -RootDirectory $PrimaryIndexLocation -IndexPartition 1
New-SPEnterpriseSearchIndexComponent –SearchTopology $SearchTopology -SearchServiceInstance $SearchAppInstance3 -RootDirectory $ReplicaIndexLocation -IndexPartition 1

Next Steps:
  • Create Content Sources and schedules 
  • Create Search centre site 
  • Configure Crawl account & search center URL for the Search service application


You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


Monday, October 9, 2017

SharePoint Online: Rename Files in Document Library using PowerShell

Requirement: Rename files in SharePoint document library using PowerShell
sharepoint online rename file in document library

PowerShell CSOM Script to rename a file in SharePoint Online:
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  
#Set Variables for Site URL, Old File Name and New File Name
$SiteURL= "https://crescent.sharepoint.com/sites/sales/"
$OldFileURL="/sites/Sales/Documents/Legal.docx"
$NewFileURL="/sites/Sales/Documents/LegalTemplate.docx"

#Setup Credentials to connect
$Cred = Get-Credential
$Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

Try {
    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Cred

    #Rename the File
    $File = $Ctx.Web.GetFileByServerRelativeUrl($OldFileURL) 
    $File.MoveTo($NewFileURL, [Microsoft.SharePoint.Client.MoveOperations]::Overwrite) 
    $Ctx.ExecuteQuery()

    write-host -f Green "File Renamed successfully!"
}
Catch {
    write-host -f Red "Error Renaming File!" $_.Exception.Message
}

This script renames the given document to new name. How about renaming all files from a SharePoint online document library?

SharePoint online rename files in bulk using PowerShell:
Say in SharePoint online, you want to rename all documents which has a specific string in their names. 
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  
#Set Variables for Site URL, Library Name and Item ID
$SiteURL= "https://crescent.sharepoint.com/sites/sales/"
$LibraryName="Documents"
$OldString="National"
$NewString="Crescent"

#Setup Credentials to connect
$Cred = Get-Credential
$Cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

Try {
    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    $Ctx.Credentials = $Cred

    #Get the web and Library
    $Web=$Ctx.Web
    $List=$web.Lists.GetByTitle($LibraryName)

    #Get all items
    $Query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery()
    $ListItems = $List.GetItems($Query)
    $Ctx.Load($ListItems)
    $Ctx.ExecuteQuery()

    #Rename each file matching given old string
    Foreach($Item in $ListItems)
    {
        #Replace string in the File Name
        if($Item["FileRef"].ToString().Contains($OldString))
        {
            $CurrentURL=$Item["FileRef"].ToString()
            $NewURL = $CurrentURL.Replace($OldString,$NewString) 
            $Item.File.MoveTo($NewURL, [Microsoft.SharePoint.Client.MoveOperations]::Overwrite) 
            $ctx.ExecuteQuery()
            Write-host -f Green "Renamed: "$CurrentURL
        }
    }
}
Catch {
    write-host -f Red "Error Renaming File!" $_.Exception.Message
} 


You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


You might also like:

Related Posts Plugin for WordPress, Blogger...