Saturday, 30 March 2019

Steps to Create a custom connector for a web API

Now you can create your own custom connector and you can also submit it to Microsoft to publish and make it available for all the regions.

Unlike the SharePoint Apps, Custom Connector don't provides the Licensing Mechanism, you have to create your own.

First you need to create Web API for which you want to create Custom Connector:

1.     Open Visual Studio, then choose File > New Project.
a.    Expand Installed, go to Templates > Visual C# > Web, and select ASP.NET Web Application.
b.    Provide a project name, location, and solution name for your app, then choose OK.
2.    In the New ASP.NET Web Application box, select the Web API template. If not already selected, select Host in the cloud. Choose Change Authentication.
3.    Select No Authentication and choose OK. You can set up authentication later.
4.    When the New ASP.NET Web Application box reappears, choose OK.
5.    Create App Service box, you can skip this step.
6.    You can create web App on azure and download the Publishing profile and import that during the publish also copy Client id and generate Client Secrete while creating web app on azure.
7.    After Visual Studio deploys your project, build the code for your app.

Create an OpenAPI (Swagger) file that describes your Web API



To connect your Web API app to Microsoft Flow , you need an Swagger file that describes your API's operations.

Steps to create Swagger File:

  •  If you haven't already, install the Swashbuckle Nuget package in your Visual Studio project.


a.    In Visual Studio, choose Tools > NuGet Package Manager > Package Manager Console.
b.    In the Package Manager Console, go to your app's project directory if you're not there already (run Set-Location "project-path"), and run this PowerShell cmdlet:
Install-Package Swashbuckle

For example:
When you will run your app after installing Swashbuckle, Swashbuckle generates an OpenAPI file at this URL:
https://{your-web-api-app-root-URL}/swagger/docs/v1

Swashbuckle also generates a user interface at this URL:
https://{your-web-api-app-root-URL}/swagger


  • When you're ready, publish your Web API app to Azure. To publish from Visual Studio, right-click your web project in Solution Explorer, choose Publish..., and follow the prompts.
  • Get the OpenAPI document by browsing to this location:
  • http://{your-web-api-app-root-URL}/swagger/docs/v1
  • Save the content as a JSON file. Based on your browser, you might have to copy and paste the text into an empty text file.

Set up Azure Active Directory authentication

 

You will have to create two Azure Active Directory (AAD) applications in Azure.

First AAD application: Securing the Web API

The first AAD application is used to secure the Web API. Lets name it webAPI
  • Sign-on URL: https://login.windows.net
  • Reply URL: https://<your-root-url>/.auth/login/aad/callback
  • There is no need for a client key.
  • There is no need to delegate any permissions.
  • Copy the application ID, because you need it later.
  • if want to create multi-tenant connector you need to set Multi-tenant to yes inside the Properties of the ADD App.

Second AAD application: Securing the custom connector and delegated access

The second AAD application is used to secure the custom connector registration and acquire delegated access to the Web API protected by the first application. Name this one webAPI-customAPI .
  • Sign-on URL: https://login.windows.net
  • Reply URL: https://europe-001.consent.azure-apim.net/redirect
  • Add permissions to have delegated access to Web API.
  • Copy the application ID, because you need it later.
  • Generate a client key and copy it, because you need it later.
Here if you are planning to publish it to make available for all regions outside your organization by submitting it to Microsoft, you need to add to Reply URLs provided by Microsoft once you submit it:

https://global-test.consent.azure-apim.net/redirect
https://global.consent.azure-apim.net/redirect

Multi-tenant Custom Connector

  • if you want to create multi-tenant connector you need to set Multi-tenant to yes inside the Properties of the ADD Apps.
  • Then add client Id of the webAPI-customAPI configured in the webAPI's "knowClientApplications" array property in the manifest file. 

Add authentication to your Azure Web App

  1. Sign in to the Azure portal and then find the Azure Web App that you deployed in the first section.
  2. Select Settings, and then select Authentication / Authorization.
  3. Turn on App Service Authentication and then select Azure Active Directory. On the next blade, select Express.
  4. Select Select Existing AD App, and select the webAPI AAD application you created earlier.
  5. Then in Advanced blade Add Client id of webAPI.

Add the custom connector to Microsoft Flow


  • Firstly modify your Swagger file (Open API File ) to add the "securityDefintions "object and AAD authentication used for the Web App. The section of your OpenAPI with the host property should look like this:

       // File header should be above here...
       "host": "<your-root-url>",
       "schemes": [
            "https" //Make sure this is https!
        ],
        "securityDefinitions": {
           "AAD": {
               "type": "oauth2",
               "flow": "accessCode",
               "authorizationUrl": "https://login.windows.net/common/oauth2/authorize",
               "tokenUrl" : "https://login.windows.net/common/oauth2/token",
               "scopes": {}
              }
         },
      // The rest of the OpenAPI follows...

  • Browse to Microsoft Flow, and add a custom connector.
  • Choose Create custom connector, then choose Import an OpenAPI File.
  • Enter a name for the custom connector, then choose Continue.

Update general details

  • Once you have uploaded your Swagger File, the wizard auto-detects that you are using AAD authentication for your Web API.
  • Configure the AAD authentication for the custom connector.
  • Client IDClient ID of webAPI-CustomAPI
  • SecretClient key of webAPI-CustomAPI
  • Login URLhttps://login.windows.net
  • ResourceUriClient ID of webAPI
  • RedirectURLhttps://europe-001.consent.azure-apim.net/redirect
  • Select Create and creating a connection to the custom connector.

Additionally you can also Publish your Flow Templates along with you Custom Connector. you can submit a flow template to the gallery of templates for Microsoft Flow. 

Templates help people not only to create flows more easily but also to imagine additional scenarios that would benefit from a flow.

Steps to Submit Flow Template


  • On the My Flows page, select the ellipsis (...) for a flow which you have created using you custom connector.
  • Select Details in the menu that appears.
  • Select More, and then select Submit as template in the menu that appears.
  • You can only submit flows that have run successfully at least once since the last save.
  • Specify a meaningful title, a clear description of the scenario that your template will help automate, the biggest benefit, the number of users, number of runs, and the categories that apply for the template.
  • Select Submit.
  • The Microsoft Flow team verifies and possibly modifies your template. If the team approves your template, it appears in the gallery of templates for Microsoft Flow.

















Thursday, 13 December 2018

Power Shell Script to Export all existing sitecollection URLs and its site's URL in a SharePoint Online tenant to XML file

try
{
    Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll"
    Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll"
    Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll"
}
catch
{
    Write-Host "Problem in loading CSOM DLLs : $_.Exception.Message" -foregroundcolor black -backgroundcolor Red
    return
}

# Initialize client context
$adminURL = Read-Host -Prompt "Enter Tenant Admin URL:"
$username = Read-Host -Prompt "Enter user name:"
$password = Read-Host -Prompt "Enter password:" -AsSecureString
#$sideLoadingGuid = new-object System.Guid "AE3A1339-61F5-4f8f-81A7-ABD2DA956A7D"


try{
        function Get-SPOWebs($webURL){
            #Get Web information and subsites
            $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webURL)
            $clientContext.Credentials = $credentials
            $Web = $clientContext.Web
            $clientContext.Load($Web)
            $clientContext.Load($Web.Webs)  
            $clientContext.executeQuery()
 
            #Do something with the current sub-site
            #Write-host $Web.URL
 
            #Iterate through each subsite in the current web
            foreach ($Subweb in $Web.Webs)
            {
                if($Subweb.AppInstanceId -eq [GUID]::Empty){
                
                    Write-Host 'Url:' $Subweb.url
                    # Append as child to an existing node
                    [System.XML.XMLElement]$newXmlSiteURLElement=$oXMLRoot.appendChild($xmlDoc.CreateElement("SiteURL"))
                    $newXmlNameTextNode = $newXmlSiteURLElement.AppendChild($xmlDoc.CreateTextNode($Subweb.url));                    
           

                    #Call the function recursively to process all subsites underneaththe current web
                    Get-SPOWebs($Subweb.url)
                }
            }
        }
}
catch{
    Write-Host "$_.Exception.Message" -foregroundcolor black -backgroundcolor Red
    return
}

try
{
  # connect/authenticate to SharePoint Online and get ClientContext object.. 
  $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($adminURL) 
  $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password) 
  $clientContext.Credentials = $credentials 
  
  # Enumerate all site collections
  $tenant = New-Object Microsoft.Online.SharePoint.TenantAdministration.Tenant($clientContext)  
  $props = $tenant.GetSiteProperties(0, $true)
  $clientContext.Load($props)
  $clientContext.ExecuteQuery()
   

        # Create a new XML File with sites root node
        [System.XML.XMLDocument]$xmlDoc=New-Object System.XML.XMLDocument        
        [System.XML.XMLElement]$oXMLRoot=$xmlDoc.CreateElement("sites")
        $xmlDoc.appendChild($oXMLRoot)                       

  foreach($sp in $props)
  {
            Write-Host 'Url:' $sp.Url

            # Append as child to an existing node
            [System.XML.XMLElement]$newXmlSiteURLElement=$oXMLRoot.appendChild($xmlDoc.CreateElement("SiteURL"))
            $newXmlNameTextNode = $newXmlSiteURLElement.AppendChild($xmlDoc.CreateTextNode($sp.Url));
                    
            #$sp.Url | out-file "C:\SiteCollectionList.xml"
   
            Get-SPOWebs($sp.Url)
  }                

        # Save File
        $xmlDoc.Save("c:\config1.xml")
}
catch
{
    Write-Host "$_.Exception.Message" -foregroundcolor black -backgroundcolor Red
    return
}

Switching Between New And Classic SharePoint Document Library Experiences

Applies To: SharePoint Online , SharePoint Online admin center

You may notice a change in the look and navigation of your document libraries and lists. This new experience is faster, has additional phone and tablet features, and simpler navigation. As a document library or list owner, site owner, or administrator, you may want to switch the default experience back to the previous (classic view) for a time. Keep in mind that users can change the experience in specific libraries or sites back to new if they choose.

Following are the Steps to change the New Library experience to classic mode:

1.Copy the following code and paste it into a text editor, such as Notepad.

Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll"
$MyCredentials = Get-Credential 
#Connect-SPOService -Url https://prakashinfotech-admin.sharepoint.com -credential $MyCredentials
$webUrl = 'https://prakashinfotech.sharepoint.com/sites/Form10/'
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
[Microsoft.SharePoint.Client.ClientContext]$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$clientContext.Credentials =  New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($MyCredentials.UserName,$MyCredentials.Password)
$site = $clientContext.Site;
$clientContext.load($site);
$sitefeatureguid = new-object System.Guid "E3540C7D-6BEA-403C-A224-1A12EAFEE4C4"
$site.Features.Add($sitefeatureguid, $true, [Microsoft.SharePoint.Client.FeatureDefinitionScope]::None);
$clientContext.ExecuteQuery();

2. Save the file, naming it CustomActions.ps1.

NOTE: You can use a different file name, but you must save the file as an ANSI-encoded text file whose extension is .ps1

3.Change to the directory where you saved the file.

4 .At the Windows PowerShell command prompt, type the following command:

  ./CustomActions.ps1


Following are the Steps to change the  classic mode Library experience to New Library experience:

1.Copy the following code and paste it into a text editor, such as Notepad.


#set-executionpolicy unrestricted
Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll"
$MyCredentials = Get-Credential 
Connect-SPOService -Url https://prakashinfotech-admin.sharepoint.com -credential $MyCredentials
$webUrl = 'https://prakashinfotech.sharepoint.com/sites/ProviderhostedappsRnd/'
[Microsoft.SharePoint.Client.ClientContext]$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$clientContext.Credentials =  New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($MyCredentials.UserName,$MyCredentials.Password)
$site = $clientContext.Site;
$clientContext.load($site);
$sitefeatureguid = new-object System.Guid "E3540C7D-6BEA-403C-A224-1A12EAFEE4C4"
$site.Features.Remove($sitefeatureguid, $true);
$clientContext.ExecuteQuery();
#set-executionpolicy restricted

2. Save the file, naming it CustomActions.ps1.

NOTE: You can use a different file name, but you must save the file as an ANSI-encoded text file whose extension is .ps1

3.Change to the directory where you saved the file.

4 .At the Windows PowerShell command prompt, type the following command:

  ./CustomActions.ps1

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
}