Tuesday, September 23, 2014

Access Denied: Activate Site Collection feature

Context: I deployed a SharePoint solution via PS using Add-SPSolution and Install-SPSolution cmdlets. Once the solution was deployed, I navigated to the site collection for activating site collection feature (Site Settings -> Site Collection features). When I clicked on the respective feature for activating it, I got an error saying that Error: Access Denied. It was strange because I had the necessary right.

Solution: The solution I came up with is to activate the site collection feature by PowerShell (PS). So I wrote the following script;

$mySite = Get-SPSite http://sharepointsite.com
Enable-SPFeature -Identity "Feature Name" -Url $mySite.Url
$mySite.Dispose()


Once I executed the above command, a new error occurred saying that “The Feature is not a Farm Level Feature and is not found in a Site level defined by the Url http://sharepointsite.com”.
In order to solve this issue, I decided to execute the above command(Enable-SPFeature) by providing the feature id. As we know .wsp is a cab file so I decided to extract .wsp in order to get feature.xml file. From their, I got the feature id of the respective feature and executed the command by providing feature id without double quotes. It executed flawlessly and my webpart was ready to be used in the site. Here is the following command that I used;

Enable-SPFeature -Identity  22224bdf-0raa-4a7a-b96e-5b2fd65e3604  -Url $mySite.Url


Cheers… J..I hope it helps.

Monday, September 15, 2014

Working with Network drive via PowerShell(2.0, 3.0 & 4.0)


In Windows PowerShell 3.0 & 4.0, you can use New-PSDrive to access the file system location that can be a remote or on a local computer.
$pass = $password | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PsCredential($user_name,$pass)
New-PSDrive -name CSV-PATH -Root $path -Credential $cred -PSProvider filesystem 
$path = $path + "\" + "file.csv"; 

if(Test-Path $path){
   Write-Host "File found." 
}
Remove-PSDrive -Name CSV-PATH; 

On the other hand,  PowerSehll 2.0. doesnot support New-PSDrive cmdlet. In that case you can use one of the following two ways to access a network drive for reading, creating, updating and deleting a file;
$net.mapnetworkdrive($drive, $path, $true, $username, $password)
$path = $path + "\" + "file.csv"; 
Remove-item $path;
$net.RemoveNetworkDrive($drive)

$uncServer = "\\Servername";
        net use $uncServer $password /USER:$username
$path = $path + "\" + "file.csv";
Remove-item $path; 
net use $uncServer /delete

I hope this post will help someone..Cheers..J

Monday, September 8, 2014

Create and Update Site Collections by reading from Csv files(PowerShell)


Context: The requirement from the customer was to create a Power Shell script that should create Site Collections by reading data from a csv file. Creation of Site collection should include adding default groups and site owners once the site collection is created. The PS script should compile its results (success & failure)  in a csv file at the end. Once the PS script is ready, it should be scheduled via Windows task scheduler. An important requirement that have to be considered while designing the PS Script is to  resume the processing of a Site Collection where it was left (error occurred) in the last run.

Solution: By considering the requirements, It wrote a PS script that creates and processes the  site collection creation by reading data from a csv file. The script is capable of creating of site collections along with the default user groups and site owners. For instance; consider a scenario when script produces an error after creating a site collection. Let's say that error was related to the site owners. In that case, PS script is going to execute next time from the last state. To acheive such a flexibility, different status were introduced which specifies which block of code should run based on the status.
I have used Functions to ensure the usability of the code. As described earlier, the PS script generates a csv file as a log file that describes which site collection were processed successfully and which site collection had problem (error).


clear

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) 
{
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

# String Constants
$site_status_requested = "Site Requested";
$site_status_creating = "Creating Site Collection";
$site_status_creating_resolved = "Creating Site Collection - Resolved";
$site_status_url_error = "Url Not Resolved";
$site_status_url_error_resolved = "Url Issue - Resolved";
$site_status_addingGroups = "Adding Site Collection Groups";
$site_status_addingGroups_resolved = "Adding Site Collection Groups - Resolved";
$site_status_addingOwners = "Adding Site Owners";
$site_status_addingOwners_resolved = "Adding Site Owners - Resolved";
$site_status_ready = "Site Ready";


$path_csv_read = "C:\ExportCsv\Internal Team Site Requests - Export.csv";
$path_csv_write = "C:\ExportCsv\Site Collection Requests - Status.csv";
$webapplication_url = "https://test/";

# Site Collection Default values
$site_url = "https://WebApp/sites/"
$web_template = "Team Site";
$site_admin_primary = "domain\username";
$site_admin_secondary = ""; 
$site_language = 1033; # English
$site_compatibilitylevel = 15 #For SharePoint 2013


if(Test-Path $path_csv_read)
{            

    $csv_objects= Import-Csv $path_csv_read;

    $items_status_array = @()

    foreach($row in $csv_objects){

        $_sitestatus = $row."Site Status";
        $row_new = $row.PsObject.Copy();    
       
        StartProcessing -row $row -row_new $row_new;
   
        Write-Host "---------Adding in the Csv--------"; 
        Write-host $row_new.'URL Team Site'; 
        Write-host $row_new.'Status Description'; 
        Write-Host $row_new.'Site Status';
        Write-Host "-----------------";
    
        $items_status_array += $row_new;
    }

    if($items_status_array.length -gt 0)
    {        
        #Delete the export file once read.
        Remove-item $path_csv_read;
        #export the items in the csv file                 
        $items_status_array | Export-Csv -path $path_csv_write
        Write-Host "Status of all requested site collection are updated to a a csv file at : " + $path_csv_write;
    }
}

Function StartProcessing($row, $row_new){
    Try
    {        
        switch($_sitestatus)
        {            
            $site_status_requested
            {            
                Start-ProcessingNewSites -row $row -row_new $row_new;      
                
            }

            $site_status_creating
            {   
                Write-Host "site_status_creating block";         
                Start-ProcessingNewSites -row $row -row_new $row_new;      
                
            }
            $site_status_creating_resolved
            {   
                Write-Host "site_status_creating_resolved block";      
                Start-ProcessingNewSites -row $row -row_new $row_new;      
                
            }

            $site_status_url_error
            {
                
                Write-Host "site_status_url_error block";  
                StartProcessingNewSites -row $row -row_new $row_new;
            }
            $site_status_url_error_resolved
            {
                
                Write-Host "site_status_url_error_resolved block";  
                StartProcessingNewSites -row $row -row_new $row_new;
            }


            $site_status_addingGroups
            {                
                Write-Host "site_status_addingGroups block";  
                Process-AddingSiteCollectionGroups -row $row -row_new $row_new;
            }
            $site_status_addingGroups_resolved
            {                
                Write-Host "site_status_addingGroups_resolved block";  
                Process-AddingSiteCollectionGroups -row $row -row_new $row_new;
            }

            $site_status_addingOwners
            {                
                Write-Host "site_status_addingOwners block";  
                Process-AddingSiteOwners -row $row -row_new $row_new;
            } 
             $site_status_addingOwners_resolved
            {                
                Write-Host "site_status_addingOwners_resolved block";  
                Process-AddingSiteOwners -row $row -row_new $row_new;
            }             
         }        
    }
    Catch
    {
        $row_new.'Status Description' = $_.Exception.Message;
    }
}

Function Start-ProcessingNewSites($row, $row_new){  
    $_siteurl = $row.'URL Team Site';             
    if($_siteurl){                                                            
        if($_siteurl -eq $site_url){
            set_SiteStatus -row_new $row_new -site_status $site_status_url_error;
        }
        else{                  
            Process-SiteRequested -row $row -row_new $row_new;
        }
    }
    else{                                     
        set_SiteStatus -row_new $row_new -site_status $site_status_url_error;                             
    }       
}

Function Process-SiteRequested($row, $row_new){   
   Try
    {         

        $_siteurl = get_SiteUrl -siteurl $row.'URL Team Site';
        $_sitetemplate = get_WebTemplateByTitle; 

        set_SiteStatus -row_new $row_new -site_status $site_status_creating;
                              
        Try 
        {         
            $ErrorActionPreference = "Stop"; #Make all errors terminating        
            New-SPSite $_siteurl -OwnerAlias $site_Admin -Language $site_language -Template $_sitetemplate `
                     -Name $row.'Title of TeamSite' -CompatibilityLevel $site_compatibilitylevel `
                     -Description $row.'Site Description';

            $row_new.'URL Team Site' = $_siteurl;

        
            set_SiteStatus -row_new $row_new -site_status $site_status_created; 
                                 
            Create-GroupsInSite -siteurl  $_siteurl -row_new $row_new;

            # Add Site Owners to the Site collection.
            Add-UserOwners -siteurl $_siteurl -row_new $row_new;

            set_SiteStatus -row_new $row_new -site_status $site_status_ready;
         }
         Catch
         {            
            Write-Host "Caught the thrown exception";
            Write-Host $Error[0].Exception;
            $row_new.'Status Description' = $_.Exception.Message;
        }
        Finally
        {
            $ErrorActionPreference = "Continue"; #Reset the error action pref to default
        }                                               
                
    }
    Catch{
        $row_new.'Status Description' = $_.Exception.Message;              
    }
}

# Start Processing a request has a status 'Adding Site Collection Groups'
Function Process-AddingSiteCollectionGroups($row, $row_new){
    Try
    {
        $ErrorActionPreference = "Stop"; #Make all errors terminating

        $_siteurl = $row_new.'URL Team Site';
        Create-GroupsInSite -siteurl  $_siteurl -row_new $row_new;        
        # Add Site Owners to the Site collection.
        Add-UserOwners -siteurl $_siteurl -row_new $row_new;

        set_SiteStatus -row_new $row_new -site_status $site_status_ready;
    }
    Catch
    {
        $row_new.'Status Description' = $_.Exception.Message; 
    }
    Finally
    {
        $ErrorActionPreference = "Continue"; #Reset the error action pref to default
    } 
}

# Start Processing a request has a status 'Adding Site Owners'
Function Process-AddingSiteOwners($row, $row_new){
    Try
    {
        $ErrorActionPreference = "Stop"; #Make all errors terminating

        $_siteurl = $row_new.'URL Team Site';        
        # Add Site Owners to the Site collection.
        Add-UserOwners -siteurl $_siteurl -row_new $row_new;

        set_SiteStatus -row_new $row_new -site_status $site_status_ready;
    }
    Catch
    {
        $row_new.'Status Description' = $_.Exception.Message; 
    }
    Finally
    {
        $ErrorActionPreference = "Continue"; #Reset the error action pref to default
    } 
}


# The purpose of this function is to create default groups into the newly created site.
Function Create-GroupsInSite($siteurl, $row_new){
    Try
    {        
        set_SiteStatus -row_new $row_new -site_status $site_status_addingGroups; 
        $web = Get-SPWeb $siteurl
        $web.CreateDefaultAssociatedGroups($site_admin_primary, $site_admin_secondary, "");
        $web.Dispose();
        set_SiteStatus -row_new $row_new -site_status $site_status_addedGroups;        
    }
    Catch
    {                
        throw $Error[0].Exception.Message;
    }
}

Function Add-UserOwners($siteurl, $row_new){
    Try
    {        
        $web = Get-SPWeb $siteurl;

        #$row_new.'Name of Site Owner' = "fdsa";                                    

        #Full Control Role Definition
        $FullControl = $web.RoleDefinitions["Full Control"]; 
        
        set_SiteStatus -row_new $row_new -site_status $site_status_addingOwners; 
        
        #Get all Owner groups with Full Control permission
        $web.Groups|?{$_.Name -match "Owners"}|%{
 
            $IsGroupFullControl = $_.Roles|?{$_.Name -eq $FullControl.Name;}
            if($IsGroupFullControl)
            {
                $SPGroup = $web.SiteGroups[$_.Name]
                if($SPGroup){      
                    $_user = $web.EnsureUser($row_new.'Name of Site Owner');
                    $_user1 = $web.EnsureUser($row_new.'Name of Site Owner 2');
                    #$_user1 = "www";
                    #Write-Host "User 1 found: " $_user1;
                    #If the both Site Owners are same users.
                    if($_user -eq $_user1)
                    {
                        $SPGroup.AddUser($_user) ;
                    }
                    else
                    {                                                                                                          
                        $SPGroup.AddUser($_user) ;                                                                                                    
                        $SPGroup.AddUser($_user1);
                    }
                }
            } 
        }
        
        set_SiteStatus -row_new $row_new -site_status $site_status_addedOwners;
      
    }
    Catch
    {
        $_errorMessage = $_.Exception.Message;             
        Throw $_errorMessage;
    }
    Finally
    {
        $web.Dispose();
    }
}


Function get_SiteUrl($siteurl){
    Try
    {       
        $status_split = $siteurl.Split("/");
        $site_name =  $status_split[$status_split.Length - 1];
        $url = $site_url + $site_name;    
        return $url;
    }
    Catch{
        set_SiteStatus -row_new $row_new -site_status $site_status_url_error;        
    }   
}

Function get_WebTemplateByTitle(){
    return Get-SPWebTemplate | Where-Object{$_.Title -eq $web_template}
}


Function set_SiteStatus($row_new, $site_status){
    $row_new.'Site Status' = $site_status;
    $row_new.'Status Description' = $site_status    
}

Read Csv File and Update SharePoint List via PowerShell

    clear
    if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) 
    {
        Add-PSSnapin "Microsoft.SharePoint.PowerShell"
    }

    # String Constants
    $path_csv_read = "C:\Users\maliks\Desktop\ExportCsv\Items - Status.csv";
    $list_name = "Internal Team Site Requests";
    $spweb_name = "https://test/sites/82411";
    $csv_objects= Import-Csv $path_csv_read

    $web = Get-SPWeb $spweb_name;
    $list = (Get-SPWeb $spweb_name).Lists.TryGetList($list_name);


    foreach($row in $csv_objects){
        $item_object = $list.Items.GetItemById($row.ID);
        $item_object["Site Status"] = $row."Site Status";
        $item_object["Status Description"] = $row.'Status Description';
        $item_object["URL Team Site"] = $row."URL Team Site";
        $item_object.Update();
    }
    $web.Dispose();
    Remove-Item $path_csv_read 

Read List items and write them into a csv file via PowerShell


I wrote a PowerShell script which can read list items whose status is false in the ‘site created’ column of my list and later write them into a csv file. The following is the PowerShell script I wrote to achieve this task.


    clear
    if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) 
    {
        Add-PSSnapin "Microsoft.SharePoint.PowerShell"
    }

    $spweb_name = "https://test/sites/4511";
    $list_name = "Site Collection Request list";
    $path_csv_write = "C:\Users\malik\Desktop\PSScripts\ExportCsv\";

    $web = Get-SPWeb $spweb_name;
    $list = (Get-SPWeb $spweb_name).Lists.TryGetList($list_name);
    $items = $list.Items;

    $exportlist = @()

    $filtereditems = $list.Items | Where-Object{[System.Convert]::ToBoolean($_["Site created"]) -eq $False}
    Write-Host "Number of items found: " $filtereditems.Count;  

    foreach($item in $filtereditems){        
           
        $ps_obj = New-Object PSObject -Property @{
                   “ID” = $item["ID"]
                   “Site Title” = $item["Site Title"]
                   "Url Team Site" = $item["URL Team Site"]
                   "Site Owner" = $item["Site Owner"]
                   
                    
        }
    $exportlist += $ps_obj 
    }

    $expath = $path_csv_write + $list_name + ' - Export.csv' 

    if(Test-Path $expath)
    {
        Remove-Item $expath
    }

    if($exportlist.length -gt 0){
             
        $exportlist | Export-Csv -path $expath
        Write-Host "New requests for Team Sites are exported to a a csv file at : " + $expath;
    }
    $web.Dispose();
I hope this will help some one.
Add and Update List Item into Replica List via List Workflow

Context: The customer has a SharePoint list which manages requests for creating new site collection. In this regard, a mechanism was needed by which new or existing list item can be copied to another internal list (Replica list).

Solution: It can be achieved by writing an event receiver on the list but the customer was not interested in a custom solution. So a workflow was created and attached to the list on item added and item updated events.

The following snapshots will show you, how I have achieved the respective results;




Open SharePoint Designer 2013 and select your list. In my case it is ‘Site Collection Request list’. Now create new list workflow.
  1. Add the ‘if current item field….’ Condition and set parameters as shown in the following figure;


2. Now add ‘Update ListItem’ object from actions object list and configure it as follows;


3. Once you are done with it, the workflow is capable of updating the list items into the replica list whenever any change occur in the original list's list item.
Follow the preceding process for making the workflow capable to create new list items into the replica list. The whole workflow is shown in the following figure;