Sunday, June 5, 2016

Getting the files modified by a specified user

If you have any requirement to find out the files which has been modified by a specific user in sharepoint online.

Then here is the powershell script which will serve the purpose.

param
(
    [Parameter(Mandatory=$true)]
    [string]$SPSite,

    [Parameter(Mandatory=$true)]
    [string]$LoginName
)

function GetWebFiles($ctx, $web, $userId)

    Write-Host
    write-host "SITE: $($web.Url)"

    # Create a new custom object to hold our result.
    $siteObject = new-object PSObject

    # Add our data to $contactObject as attributes using the add-member commandlet
    $siteObject | add-member -membertype NoteProperty -name "Site URL" -Value $($web.Url)

    # Save the current $contactObject by appending it to $resultsArray ( += means append a new element to ‘me’)
    $resultsarray += $siteObject
    
    $strQuery = "<View Scope='RecursiveAll'><Query><Where><And>
                <Eq><FieldRef Name='FSObjType' /><Value Type='Integer'>0</Value></Eq>
                <Or><Eq><FieldRef ID='Author' LookupId='True' /><Value Type='Lookup'>$($user.Id)</Value></Eq>
                <Eq><FieldRef ID='Editor' LookupId='True' /><Value Type='Lookup'>$($user.Id)</Value></Eq></Or>
                </And></Where></Query></View>"                  
     
    $lists = $web.Lists
        
    $ctx.Load($lists)
    $ctx.ExecuteQuery()

    foreach ($l in $lists)
    {       
        if ($l.BaseType.ToString() -eq "DocumentLibrary")
        {
            $camlQuery = new-object Microsoft.SharePoint.Client.CamlQuery
            $camlQuery.ViewXml = $strQuery

            $items = $l.GetItems($camlQuery)

            $ctx.Load($items)
            $ctx.ExecuteQuery()
       
            if ($items.Count -gt 0)
            {
                foreach ($item in $items)
                {
                    Write-Host "$($SPSite)$($item["FileRef"])" #"$($web.Url)$($item["FileRef"])"

                    # Create a new custom object to hold our result.
                    $siteObject = new-object PSObject

                    # Add our data to $contactObject as attributes using the add-member commandlet
                    $siteObject | add-member -membertype NoteProperty -name "Site URL" -Value "$($web.Url)$($item["FileRef"])"

                    # Save the current $contactObject by appending it to $resultsArray ( += means append a new element to ‘me’)
                    $resultsarray += $siteObject
                }
            }
        }       
    }
   
    $subWebs = $web.Webs

    $ctx.Load($subWebs)
    $ctx.ExecuteQuery()

    foreach ($subWeb in $subWebs)
    {
        GetWebFiles $ctx $subWeb $userId
    }
}

$username = Read-Host -Prompt "Enter Username"
$password = Read-Host -Prompt "Enter Password" -AsSecureString

$scriptPath = Split-Path $MyInvocation.MyCommand.Path

Add-Type -Path "$($scriptPath)\Microsoft.SharePoint.Client.dll"
Add-Type -Path "$($scriptPath)\Microsoft.SharePoint.Client.Runtime.dll"

# Declare an array to collect our result objects
$resultsarray =@()

try
{
    $teamSites = import-csv “$($scriptPath)\O365DTeamSites.csv”

    ForEach ($item in $teamSites)
    {
        $siteUrl = $item.(“Site URL”)

        #Write-Output “Site URL: $siteUrl”

        try
        {
            $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
   
            $creds = New-Object System.Net.NetworkCredential($username, $password)
            $ctx.Credentials = $creds;
                      
            $user = $ctx.Web.SiteUsers.GetByLoginName($LoginName)
                            
            $rootWeb = $ctx.Site.RootWeb  

            $ctx.Load($rootWeb)
            $ctx.Load($user)
            $ctx.ExecuteQuery()

            GetWebFiles $ctx $rootWeb $user.Id       
        }
        catch [Exception]
        {
            if($($_.Exception.Message) -ne "User cannot be found.")
            {
                write-host "Error checking site: $($siteUrl) -> $($_.Exception.Message)"
            }
            continue;
        }
    }

    $resultsarray| Export-csv $($scriptPath)\UserSites.csv -notypeinformation
}
catch [Exception]
{
    write-host "Error -> $($_.Exception.Message)"
    continue;
}


Happy Coding!!!

Retrieve My sites list in SharePoint online

When i was working on branidng for my sites, then i should get the list of my sites so that i can apply the custom branding to all the my sites.
So, in order to get the list of my sites, i have written the below code.

private static void GetMySites()
        {
            string siteUrl = "<root my site url>";
            string userName = "<Enter tenant admin user name>";
            string password = "<Enter tenant admin password>";

            SecureString securePassword = new SecureString();
            foreach (char c in password)
            {
                securePassword.AppendChar(c);
            }

            using (ClientContext clientContext = new ClientContext(siteUrl))
            {
                clientContext.Credentials = new SharePointOnlineCredentials(userName, securePassword);

                Web web = clientContext.Web;
                clientContext.Load(web);
                clientContext.ExecuteQuery();

                List<SiteEntity> mysites = web.MySiteSearch();

                Console.WriteLine("My sites count: " + mysites.Count);

                string filePath = "D:\\GetMySites\MySites.txt";
                if (!System.IO.File.Exists(filePath))
                {
                    System.IO.File.Create(filePath).Close();
                }

                StringBuilder sbSites = new StringBuilder();
                using (System.IO.TextWriter writer = System.IO.File.CreateText(filePath))
                {
                    foreach (SiteEntity site in mysites)
                    {
                        sbSites = sbSites.Append(site.Url + ";");
                    }
                    writer.WriteLine(sbSites);
                }
            }
        }


Hope this code helps you to get the list of my sites in a text file.

Happy coding :)

Update User Profile properties in SharePoint online

I had a requirement from our client that we need to update some of the user AD properties with the SharePoint user profile properties regularly.

So, for that i have build a console application to update the properties and i have scheduled it as a Windows scheduler.

I have kept all the settings (LDAP connection string, AD admin username, password etc.. ) in the app.config file so that i don't have to specify them in the code.

Below is the App.Config file code.

<appSettings>
    <add key="tenantAdministrationUrl" value=""/>
    <add key="tenantAdminLoginName" value=""/>
    <add key="tenantAdminPassword" value=""/>
    <add key="UserProfileProperties" value="WorkPhone|CellPhone"/>   
    <add key="UserProfilePrimeProperty" value="WorkEmail"/>
    <add key="LDAP" value="LDAP://OU="/>
    <add key="ADAdminUserName" value="" />
    <add key="ADAdminPassword" value="" />
    <add key="Properties" value="telephoneNumber|mobile"/>
    <add key="PrimeProperty" value="mail"/>
  </appSettings>
 


Below is the code to retrieve the settings from the app.config file.
Consts._UserProfilePrimeProperty = ConfigurationManager.AppSettings["UserProfilePrimeProperty"];
Consts.ADAdminUserName = ConfigurationManager.AppSettings["ADAdminUserName"];
Consts.ADAdminPassWord = ConfigurationManager.AppSettings["ADAdminPassWord"];
Consts._ProfileProperties = ConfigurationManager.AppSettings["Properties"];
Consts._PrimeProperty = ConfigurationManager.AppSettings["PrimeProperty"];
Consts._tenantAdministrationUrl = ConfigurationManager.AppSettings["tenantAdministrationUrl"];
Consts._tenantAdminLoginName = ConfigurationManager.AppSettings["tenantAdminLoginName"];
Consts._tenantAdminPassword = ConfigurationManager.AppSettings["tenantAdminPassword"];
Consts._UserProfileProfileProperties = ConfigurationManager.AppSettings["UserProfileProperties"];

Below method will retrieve the AD information.

 
public static void getADInformation()
        {
            string ldap = ConfigurationManager.AppSettings["LDAP"];
            Console.WriteLine(ldap);
            if (!string.IsNullOrEmpty(ldap))
            {
                try
                {
                    using (DirectoryEntry ou = new DirectoryEntry(ldap))
                    {
                        DirectorySearcher searcher = new DirectorySearcher(ou);
                        searcher.Filter = "(&(objectClass=user)(objectCategory=person))";
                        searcher.SearchScope = SearchScope.Subtree;
                        searcher.PageSize = 1000;//This has to be set to return all the results from AD, if not set then it will limit the results to 1000 only

                        string[] tempProp = Consts._ProfileProperties.Split('|');
                        string[] properties = new string[tempProp.Count() + 1];
                        properties[0] = Consts._PrimeProperty;
                        for (int i = 1; i <= tempProp.Count(); i++)
                        {
                            properties[i] = tempProp[i - 1];
                        }

                        foreach (var property in properties)
                        {
                            searcher.PropertiesToLoad.Add(property);
                        }

                        SearchResult result;
                        List<string[]> columnValues = new List<string[]>();

                        searcher.PropertiesToLoad.Add("userPrincipalName");
                        searcher.PropertiesToLoad.Add("samAccountName");

                        SearchResultCollection resultCol = searcher.FindAll();
                        Console.WriteLine("Find all");
                        if (resultCol != null)
                        {
                            Console.WriteLine(resultCol.Count + " Results found in AD");
                            for (int i = 0; i < resultCol.Count; i++)
                            {
                                result = resultCol[i];
                                Console.WriteLine(result.Properties["samAccountName"][0] + "-" + result.Properties["userPrincipalName"][0]);

                                string[] tempPropVal = new string[properties.Count()];
                                int propCnt = 0;
                                //get each property value to array
                                foreach (var property in properties)
                                {
                                    if (result.Properties.Contains(property))
                                    {
                                        tempPropVal[propCnt] = Convert.ToString(result.Properties[property][0]);
                                    }
                                    else
                                    {
                                        tempPropVal[propCnt] = "";
                                    }
                                    propCnt++;
                                }
                                columnValues.Add(tempPropVal);
                            }
                        }

                        string[] tempUserProfileProp = Consts._UserProfileProfileProperties.Split('|');
                        string[] userProfileProperties = new string[tempUserProfileProp.Count() + 1];
                        userProfileProperties[0] = Consts._UserProfilePrimeProperty;
                        for (int i = 1; i <= tempUserProfileProp.Count(); i++)
                        {
                            userProfileProperties[i] = tempUserProfileProp[i - 1];
                        }

                        Consts.UserProdDataCol = new List<Dictionary<string, ExpandoObject>>();

                        foreach (string[] columnValue in columnValues)
                        {
                            dynamic data = new ExpandoObject();
                            //get the first object which is the account
                            string account = columnValue[0];
                            Dictionary<string, ExpandoObject> userProfData = new Dictionary<string, ExpandoObject>();
                            if (!string.IsNullOrEmpty(account))
                            {
                                for (int j = 1; j < userProfileProperties.Count(); j++)
                                {
                                    ((IDictionary<String, Object>)data).Add(userProfileProperties[j], columnValue[j]);
                                }
                                userProfData.Add(account, data);
                                Consts.UserProdDataCol.Add(userProfData);
                            }
                            else
                            {
                                Console.WriteLine(account + " is empty");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.StackTrace);
                }
            }
        }

Below is the method which will import AD data to user profile.

public static void ImportDatatoCloud(TextWriter writer)
        {
            List<Dictionary<string, ExpandoObject>> dataCollection = Consts.UserProdDataCol;
            int count = 1;
            int totalUsers = dataCollection.Count;
            foreach (var dataCol in dataCollection)
            {
                Generic.LogMessage(string.Format("processing user '{0}' of {1}...", count, totalUsers), Consts.LogLevel.Information);
                var userName = dataCol.Keys.FirstOrDefault();
                dynamic propertySet = dataCol.Values.FirstOrDefault();

                string UserAccountName = Consts._sPOProfilePrefix + userName;

                try
                {
                    using (ClientContext clientContext = new ClientContext(Consts._tenantAdministrationUrl))
                    {
                        SecureString passWord = new SecureString();

                        foreach (char c in Consts._tenantAdminPassword.ToCharArray()) passWord.AppendChar(c);

                        clientContext.Credentials = new SharePointOnlineCredentials(Consts._tenantAdminLoginName, passWord);

                        // Get the people manager instance for tenant context
                        PeopleManager peopleManager = new PeopleManager(clientContext);

                        foreach (var property in propertySet)
                        {
                            string propName = property.Key;
                            string propVal = property.Value;
                            Console.WriteLine(userName + " - " + propName + "->" + propVal);

                            if (propVal.Contains('|'))
                            {
                                // List Multiple values
                                List<string> propValues = propVal.Split('|').ToList<string>();

                                // Update the SPS-Skills property for the user using account name from profile.
                                peopleManager.SetMultiValuedProfileProperty(UserAccountName, propName, propValues);
                            }
                            else
                            {
                                // Update the AboutMe property for the user using account name.
                                peopleManager.SetSingleValueProfileProperty(UserAccountName, propName, propVal);
                            }
                        }

                        clientContext.ExecuteQueryRetry();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Issue in updating the profile properties for :" + UserAccountName);
                    writer.WriteLine("Issue in updating the profile properties for :" + UserAccountName);
                    writer.WriteLine(ex.ToString());
                }

                count++;
            }
            Generic.LogMessage("Processing finished for " + totalUsers + " user profiles. Import Complete!", Consts.LogLevel.Warning);
        }

Points to remember: 
Remember to add the sharepoint online client dll's - Microsoft.SharePoint.Client.dll, Microsoft.SharePoint.Client.Runtime.dll, Microsoft.SharePoint.Client.UserProfiles.dll

Hope this code helps you.

Happy Coding :)



Thursday, October 16, 2014

Create and Configure Search Service Application in SharePoint 2013 using PowerShell



The core search architecture of SharePoint 2013 has a more complex and flexible topology that can be changed more efficiently by using Windows PowerShell. Each Search service application has its own search topology. If you create more than one Search service application in a farm, it’s recommended to allocate dedicated servers for the search topology of each Search service application.

In this blog, we will see how to configure topology for one search service application with multiple search components across 2 servers for redundancy and performance.

#==============================================================
#Search Service Application Configuration Settings
#==============================================================

$SearchApplicationPoolName = " SearchApplicationPool"$SearchApplicationPoolAccountName = "Contoso\Administrator"$SearchServiceApplicationName = "Search Service Application"$SearchServiceApplicationProxyName = "Search Service Application Proxy"$DatabaseServer = "2013-SP"$DatabaseName = "SP2013 Search"$IndexLocationServer1 = "D:\SearchIndexServer1"mkdir -Path $IndexLocationServer1 -Force$IndexLocationServer2 = "D:\SearchIndexServer2"mkdir -Path $IndexLocationServer2 -Force

#==============================================================
#Search Application Pool
#==============================================================

Write-Host -ForegroundColor DarkGray "Checking if Search Application Pool exists"$SPServiceApplicationPool = Get-SPServiceApplicationPool -Identity$SearchApplicationPoolName -ErrorAction SilentlyContinueif (!$SPServiceApplicationPool){ Write-Host -ForegroundColor Yellow "Creating Search Application Pool"$SPServiceApplicationPool = New-SPServiceApplicationPool -Name$SearchApplicationPoolName -Account $SearchApplicationPoolAccountName -Verbose}

#==============================================================
#Search Service Application
#==============================================================

Write-Host -ForegroundColor DarkGray "Checking if SSA exists"$SearchServiceApplication = Get-SPEnterpriseSearchServiceApplication-Identity $SearchServiceApplicationName -ErrorAction SilentlyContinueif (!$SearchServiceApplication){ Write-Host -ForegroundColor Yellow "Creating Search Service Application"$SearchServiceApplication = New-SPEnterpriseSearchServiceApplication -Name$SearchServiceApplicationName -ApplicationPool $SPServiceApplicationPool.Name-DatabaseServer $DatabaseServer -DatabaseName $DatabaseName}Write-Host -ForegroundColor DarkGray "Checking if SSA Proxy exists"$SearchServiceApplicationProxy = Get-SPEnterpriseSearchServiceApplicationProxy-Identity $SearchServiceApplicationProxyName -ErrorAction SilentlyContinueif (!$SearchServiceApplicationProxy){ Write-Host -ForegroundColor Yellow "Creating SSA Proxy"New-SPEnterpriseSearchServiceApplicationProxy -Name$SearchServiceApplicationProxyName -SearchApplication$SearchServiceApplicationName}

#==============================================================
#Start Search Service Instance on Server1
#==============================================================

$SearchServiceInstanceServer1 = Get-SPEnterpriseSearchServiceInstance -local Write-Host -ForegroundColor DarkGray "Checking if SSI is Online on Server1" if($SearchServiceInstanceServer1.Status -ne "Online") { Write-Host -ForegroundColor Yellow "Starting Search Service Instance" Start-SPEnterpriseSearchServiceInstance -Identity $SearchServiceInstanceServer1 While ($SearchServiceInstanceServer1.Status -ne "Online") { Start-Sleep -s 5 } Write-Host -ForegroundColor Yellow "SSI on Server1 is started" }

#==============================================================
#Start Search Service Instance on Server2
#==============================================================

$SearchServiceInstanceServer2 = Get-SPEnterpriseSearchServiceInstance -Identity
"2013-SP-AFCache" Write-Host -ForegroundColor DarkGray "Checking if SSI is Online on Server2" if($SearchServiceInstanceServer2.Status -ne "Online") { Write-Host -ForegroundColor Yellow "Starting Search Service Instance" Start-SPEnterpriseSearchServiceInstance -Identity $SearchServiceInstanceServer2 While ($SearchServiceInstanceServer2.Status -ne "Online") { Start-Sleep -s 5 } Write-Host -ForegroundColor Yellow "SSI on Server2 is started" }

#==============================================================
#Cannot make changes to topology in Active State.#Create new topology to add components
#============================================================== $InitialSearchTopology = $SearchServiceApplication |
Get-SPEnterpriseSearchTopology -Active
$NewSearchTopology = $SearchServiceApplication | New-SPEnterpriseSearchTopology

#==============================================================
#Search Service Application Components on Server1#Creating all components except Index (created later)
#==============================================================

New-SPEnterpriseSearchAnalyticsProcessingComponent -SearchTopology
$NewSearchTopology -SearchServiceInstance $SearchServiceInstanceServer1 New-SPEnterpriseSearchContentProcessingComponent -SearchTopology
$NewSearchTopology -SearchServiceInstance $SearchServiceInstanceServer1 New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology
$NewSearchTopology -SearchServiceInstance $SearchServiceInstanceServer1 New-SPEnterpriseSearchCrawlComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer1
New-SPEnterpriseSearchAdminComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer1

#==============================================================
#Search Service Application Components on Server2.#Crawl, Query, and CPC
#==============================================================

New-SPEnterpriseSearchContentProcessingComponent -SearchTopology
$NewSearchTopology -SearchServiceInstance $SearchServiceInstanceServer2 New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology
$NewSearchTopology -SearchServiceInstance $SearchServiceInstanceServer2 New-SPEnterpriseSearchCrawlComponent -SearchTopology
$NewSearchTopology -SearchServiceInstance $SearchServiceInstanceServer2


Server1
Primary
Server2
Primary
IndexPartition 0
IndexComponent 1

True
IndexComponent 2
False
IndexPartition 1
IndexComponent 3
False

IndexComponent 4

True
IndexPartition 2
IndexComponent 5

True
IndexComponent 6
False
IndexPartition 3
IndexComponent 7
False
IndexComponent 8

True



#==============================================================
#Index Components with replicas
#==============================================================

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer1 -IndexPartition 0
-RootDirectory $IndexLocationServer1

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer2 -IndexPartition 0
-RootDirectory $IndexLocationServer2

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer2 -IndexPartition 1
-RootDirectory $IndexLocationServer2

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer1 -IndexPartition 1
-RootDirectory $IndexLocationServer1

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer1 -IndexPartition 2
-RootDirectory $IndexLocationServer1

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer2 -IndexPartition 2
-RootDirectory $IndexLocationServer2

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer2 -IndexPartition 3
-RootDirectory $IndexLocationServer2

New-SPEnterpriseSearchIndexComponent -SearchTopology $NewSearchTopology
-SearchServiceInstance $SearchServiceInstanceServer1 -IndexPartition 3
-RootDirectory $IndexLocationServer1

#==============================================================
#Setting Search Topology using Set-SPEnterpriseSearchTopology
#==============================================================
Set-SPEnterpriseSearchTopology -Identity $NewSearchTopology

#==============================================================
#Clean-Up Operation
#==============================================================
Write-Host -ForegroundColor DarkGray "Deleting old topology"
Remove-SPEnterpriseSearchTopology -Identity $InitialSearchTopology
-Confirm:$false
Write-Host -ForegroundColor Yellow "Old topology deleted"

#==============================================================
#Check Search Topology
#==============================================================
Get-SPEnterpriseSearchStatus -SearchApplication $SearchServiceApplication -Text
Write-Host -ForegroundColor Yellow "Search Service Application and Topology
is configured!!"
$Server02 = (Get-SPServer "2013-SPHost2").Name
$EnterpriseSearchserviceApplication = Get-SPEnterpriseSearchserviceApplication
$ActiveTopology = $EnterpriseSearchserviceApplication.ActiveTopology.Clone()
$IndexComponent =(New-Object Microsoft.Office.Server.Search.Administration.Topology.IndexComponent
$Server02,1);
$IndexComponent.RootDirectory = "D:\IndexComponent02"



# Server1 is the local server where the script is run.

For fault-tolerance we need to have at least two index components (replicas) for an index partition. Here I create 4 index partition with 8 index components. One index partition can serve up to 10 million items. As a good practice, the primary and secondary replicas should be balanced among the index servers. So we will have Server1 hosting 4 index component (out of which 2 will be primary replicas) and Server2 hosting other 4 index components (2 primary replicas).



* Please note that above cmdlets will not create the primary replicas in the server we want as expected as we are running all the cmdlets at same time without saving the topology. Ideally we should create an index partition in one server and then run Set-SPEnterpriseSearchTopology. This will ensure that the primary replica is created in the server we want. The next time you run the same cmdlet in another server for same index partition will create secondary replica. For more details - http://blogs.technet.com/b/speschka/archive/2012/12/02/adding-a-new-search-partition-and-replica-in-sharepoint-2013.aspx

When the above script is run one after the other to create multiple index partitions and replicas in different servers, you can see in the picture below there is no particular order for creation of replicas in the servers. The Primary and secondary replicas are not created in the servers that we wanted. If you are concerned about primary index component server location, then you should set the topology before you run the cmdlet to create secondary replica in another server.






Now that all search components are created in our new topology. Before setting our new topology we need to activate this topology. Remember that we also have an old topology which is in active state.



We will use Set-SPEnterpriseSearchTopology cmdlet which does some important tasks - Activates the NewTopology [$NewSearchTopology.Activate()], deactivates all other active topologies and sets the NewTopology(Active) as the current Enterprise Search Topology

#==============================================================
   #Setting Search Topology using Set-SPEnterpriseSearchTopology
 #==============================================================
 Set-SPEnterpriseSearchTopology -Identity $NewSearchTopology

After running Set-SPEnterpriseSearchTopology cmdlet, it will look like



As Set-SPEnterpriseSearchTopology has already done most of the job for us we will do one last thing - delete the old topology as its no longer required.

#==============================================================
                 #Clean-Up Operation
 #==============================================================
 Write-Host -ForegroundColor DarkGray "Deleting old topology"
 Remove-SPEnterpriseSearchTopology -Identity $InitialSearchTopology
 -Confirm:$false
 Write-Host -ForegroundColor Yellow "Old topology deleted"


When $SearchServiceApplication | Get-SPEnterpriseSearchTopology cmdlet is run, you will find just one topology (new topology that we created)



#==============================================================
                 #Check Search Topology
 #==============================================================
 Get-SPEnterpriseSearchStatus -SearchApplication $SearchServiceApplication -Text
 Write-Host -ForegroundColor Yellow "Search Service Application and Topology
 is configured!!"

In Central administration, Search service application you will find topology like this;



In your environment to know the numbers for each search components, use this scaling guidelines.



* The New-SPEnterpriseSearchIndexComponent requires folder for storing index files. In multiple server search configuration scenario, New-SPEnterpriseSearchIndexComponent checks the existence of RootDirectory in the wrong server. It checks the existence of the folder only in the machine where PowerShell script is executed; even in those cases when new index component is scheduled for other machine. You will get an error message, New-SPEnterpriseSearchIndexComponent : Cannot bind parameter 'RootDirectory'

There are 2 workarounds for this;

1. Create the folder manually in the machine that runs powershell script as its done in the aforementioned script.

2. Use directly the SP Object model instead of cmdlets.

$Server02 = (Get-SPServer "2013-SPHost2").Name
 $EnterpriseSearchserviceApplication  = Get-SPEnterpriseSearchserviceApplication
 $ActiveTopology = $EnterpriseSearchserviceApplication.ActiveTopology.Clone()
 $IndexComponent =(New-Object Microsoft.Office.Server.Search.Administration.Topology.IndexComponent
 $Server02,1);
 $IndexComponent.RootDirectory = "D:\IndexComponent02"
 $ActiveTopology.AddComponent($IndexComponent)

* A note on removing an index component - If you have more than one active index replica for an index partition, you can remove an index replica by performing the procedure Remove a search component in the article Manage search components in SharePoint Server 2013. You cannot remove the last index replica of an index partition using this procedure. If you have to remove all index replicas from the search topology, you must remove and re-create the Search service application and create a completely new search topology that has the reduced number of index partitions.





Tuesday, October 14, 2014

Get List item values in ItemAdding event handler

In an Item Adding event handler Properties.ListItem will be null. Same is the case with Properties.BeforeProperties & Properties.AfterProperties too. So how to get the values entered in the NewForm.aspx in the ItemAdding method. Here we go..
You can access the value using AfterProperties property. But here you have to pass the internal name of the column whose value you want to access.
properties.AfterProperties["Internal Name of the column"]
Or in case if you don’t know/want to explicitly specify the internal name you can get the same using Field.InternalName property. The below code snippet I used explains the same.
string employeeInternalName = Convert.ToString(site.Lists[properties.ListId].Fields["EmployeeName"].InternalName);
string employeeName= Convert.ToString(properties.AfterProperties[employeeInternalName]);

Monday, October 13, 2014

Verify database upgrades in SharePoint 2013



Verify upgrade status for databases

You can use the following methods to verify upgrade:
  • Use the Upgrade Status page in Central Administration
    This page lists all farm, service, or content database upgrades and their statuses. This includes a count of errors or warnings.
  • Review the log files to look for errors or warnings
    If upgrade was not successfully completed, you can view the log files to find the issues, address them, and then restart the upgrade process.

Review the log files for database attach upgrade

To verify that upgrade has succeeded, you can review the following log and error files:
  • The upgrade log file and the upgrade error log file.
    Review the upgrade log file and the upgrade error log file (generated when you run the upgrade). The upgrade log file and the upgrade error log file are located at %COMMONPROGRAMFILES%\Microsoft Shared\Web server extensions\15\LOGS. The logs are named in the following format: Upgrade-YYYYMMDD-HHMMSS-SSS.log, where YYYYMMDD is the date and HHMMSS-SSS is the time (hours in 24-hour clock format, minutes, seconds, and milliseconds). The upgrade error log file combines all errors and warnings in a shorter file and is named Upgrade-YYYYMMDD-HHMMSS-SSS-error.log.

Check upgrade status for databases

The Upgrade Status page lists the upgrade sessions and gives details about the status of each session — whether it succeeded or failed, and how many errors or warnings occurred for each server. The Upgrade Status page also includes information about the log and error files for the upgrade process and suggests remedies for issues that might have occurred.
To view upgrade status in SharePoint Central Administration
  1. Verify that you have the following administrative credentials:
    • To use SharePoint Central Administration, you must be a member of the Farm Administrators group.
  2. On the Central Administration home page, in the Upgrade and Migration section, click Check upgrade status.

Validate the upgraded environment

After you determine whether upgrade was completed successfully, validate your environment. Review the following items:
  • Service applications
    • Are they configured correctly?
    • Are the service application proxies configured the way that we want?
    • Do we have to create new connections between farms?
  • Site collections
    • Are sites that were not upgraded working as expected in 2010 mode?
    • Are all features associated with the sites working?
  • Search
    • Run a crawl, and review the log files.
    • Run search queries, and verify that the queries work as expected and provide appropriate results. Twenty-four hours later, view the query reports and look for issues.
    • Search for people and profiles.
    • Check any Search customizations to make sure that they work as expected.