Sunday, 11 February 2018

Steps to install SharePoint Provider Hosted Add-in on multiple sites automatically

Automating add-in/app installation:

Pre-requisites:

  • Add-in/app which being installed has to request tenant level permissions, which is the reason why this is not suitable model for store add-ins/apps
  • Add-in/app which being installed has to be instantiated or specifically trusted by the tenant administrator once before it can be pushed to sites
  • When you install add-in/app to the site, you will need to enable so called site loading feature.
  • SharePoint hosted Add-in/app will not work, this must be provider hosted Add-in/app.

Following are the steps:

  1. Add app in app catalog.
  2. Install App in one of the site collection and grant permission (can be app catalog site also) so that same App can be installed on all other site collection programmatically without need of granting permissions on each.
  3. Run power shell script(script is given below ) to install app on each of needed site in site collection. For new sites created, script has to run again for that specific new site.
  4. Now run the power shell script.(script is given below)
  5. Now give the following information:·         
  6. Enter URL: <Url of your site collection>
  7. Enter User Name: <Valid User name the sitecollection>
  8. Enter Password: <Password>
  9. Enter app file’s Path: <Location of the app file where it is stored on your machine>
  10. Enter app config file’s Path: <Location of the app configfile where it is stored on your machine> it is xml file which contains the List of site’s relative Urls on where you want to install the app. Format for this file is shown below.
                    Name of this file: AppConfig.xml


Power Shell Script:


try
{
    $loadInfo1 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
    $loadInfo2 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
}
catch
{
    Write-Host "Problem in loading CSOM DLLs : $_.Exception.Message" -foregroundcolor black -backgroundcolor Red
    return
}

$url = Read-Host -Prompt "Enter URL:"
$username = Read-Host -Prompt "Enter user name:"
$password = Read-Host -Prompt "Enter password:" -AsSecureString 
$appfilepath = Read-Host -Prompt "Enter app file's Path:"
$ConfigXmlPath = Read-Host -Prompt "Enter app config file's Path:"
$productId = New-Object Guid("<Replce this text with your app's product ID>") 
$Update = Read-Host -Prompt "Do you want to update the app if already installed? Enter [yes or no]:"
$sideLoadingGuid = new-object System.Guid "AE3A1339-61F5-4f8f-81A7-ABD2DA956A7D"
try
{
    Write-Host "Authenticate tenant site $url and get ClientContext object" -foregroundcolor black -backgroundcolor yellow
    $context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
    $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
    $context.Credentials = $credentials
    $web = $context.Web
    $site = $context.Site
    $context.Load($site)
 $context.ExecuteQuery()
 $site.Features.Add($sideLoadingGuid, $true, [Microsoft.SharePoint.Client.FeatureDefinitionScope]::None);
    $context.Load($web)
 $context.ExecuteQuery()
    Write-Host "Authentication to online site $url and 
 get ClientContext DLLS succeeful" -foregroundcolor black -backgroundcolor Green
}
catch
{
    Write-Host "Unable to authenticate to online site. Error : $_.Exception.Message" -foregroundcolor black -backgroundcolor Red
    return
}

try
{
 Write-Host "Reading the Config Values..." -ForegroundColor Green 
    # Get the Content of the Config Xml
    [Xml]$SiteConfig = Get-Content $ConfigXmlPath   
 #$site.Features.Add($sideLoadingGuid, $true, [Microsoft.SharePoint.Client.FeatureDefinitionScope]::None);  
  
 foreach( $SiteUrl in $SiteConfig.Sites.SiteURL) 
 { 
        try
        {
            $appIoStream = New-Object IO.FileStream($appfilepath,[System.IO.FileMode]::Open)
      $site = $context.Site
      $context.Load($site)
      $context.ExecuteQuery()
         $oweb = $context.Site.OpenWeb($SiteUrl)
      $context.Load($oweb)
            Write-Host "Processing is started on site :: $SiteUrl" 
      $context.ExecuteQuery()
      $appInstance = $oweb.LoadAndInstallApp($appIoStream)
      $context.Load($appInstance)
      $context.ExecuteQuery()
      #Write-Host $appInstance.Id 
            Write-Host "Installation started..." -ForegroundColor Green 
            
            $loopon = $true
            do
            {
                Start-Sleep -s 10
                $context.Load($AppInstance)
                $context.ExecuteQuery()
                
                if($AppInstance.Status -eq "Installed")
                {
                    $loopon = $false
                }
            }
            while ($loopon)
            Write-Host "Installed successfully." -ForegroundColor Green
        }
        catch [System.Management.Automation.MethodInvocationException]
        {
            #handle app already installed error
         #$appIoStream.Dispose()
            if($_.Exception.Message -match "An instance of this App already exists at the specified location.")
            {
                Write-Host "An instance of this app is already exists." -foregroundcolor yellow                
                if($Update -eq "Yes")
                {                    
                    $appinstances = [Microsoft.SharePoint.Client.AppCatalog]::GetAppInstances($Context,$oweb)
                    $Context.Load($appinstances) 
                    $Context.ExecuteQuery() 
                    foreach($instance in $appinstances) 
                    {
                        if($productId -eq $instance.ProductId) 
                        {
                            $AppInstanceId=$instance.Id
                            $appInst = $oweb.GetAppInstanceById($AppInstanceId)
                            Write-Host "Uninstallation of this app is started..." -ForegroundColor Green
                            $appInst.Uninstall()                                                   
                            try
                            {
                                $context.Load($appInst)
                                $context.ExecuteQuery()
                                $loopon = $true                                                                
                                do
                                {
                                    try{
                                        Start-Sleep -s 10
                                        $context.Load($appInst)
                                        $context.ExecuteQuery()  
                                    }
                                    catch [System.Management.Automation.MethodInvocationException]
                                    {
                                        $loopon = $false
                                    }                                                                                       
                                }
                                while ($loopon)
                                 Write-Host "Uninstalled successfully." -ForegroundColor Green
                            }
                            catch [Net.WebException] 
                            {
                                Write-Host "Failed to uninstall the app" $_.Exception.Message.ToString() -ForegroundColor Red
                            }                            
                            $appIoStream = New-Object IO.FileStream($appfilepath,[System.IO.FileMode]::Open)
                            $appInstance = $oweb.LoadAndInstallApp($appIoStream)    
                      $context.Load($appInstance)
                      $context.ExecuteQuery()                     
                            Write-Host "Installation is started..." -ForegroundColor Green             
                            $loopon = $true
                            do
                            {
                                Start-Sleep -s 10
                                $context.Load($AppInstance)
                                $context.ExecuteQuery()
                
                                if($AppInstance.Status -eq "Installed")
                                {
                                    $loopon = $false
                                }
                            }
                            while ($loopon)
                            Write-Host "Installed successfully." -ForegroundColor Green
                                
                        }
                    }                 
                }
            }
   else
   {
    Write-Host "Unable to Install App Error : $_.Exception.Message"
   }
        }
        catch
        {
                Write-Host "Unable to Install App Error : $_.Exception.Message"
        }
 }
    Write-Host "Installation process is done on all sites listed in XML file."
 $appIoStream.close()
 $appIoStream.Dispose()
}
catch
{
 $appIoStream.Dispose()
    Write-Host "Unable to Install App Error : $_.Exception.Message" -foregroundcolor black -backgroundcolor Red
    return
}