Showing posts with label SharePoint 2016. Show all posts
Showing posts with label SharePoint 2016. Show all posts

Monday, September 17, 2018

Unified SharePoint 2013 on-premises and SPO site provisioning with SharePoint Framework (SPFx), Azure Logic Apps, Queue, Azure Function, and Azure Automation using Hybrid Runbook Worker


In previous session we have discussed Office 365 site self-provisioning with SharePoint Framework (SPFx), Azure Logic Apps, Queue, and Azure Function. In our company we are utilizing the same architecture and framework to provisioning BOTH SharePoint online site and SharePoint on-premises site. The architecture will also unify the site provisioning for SharePoint on-premises 2013, 2016, or 2019. Here is the architecture and detailed design for the implementation.

The architecture is almost identical to we implemented previous for SharePoint online. The only difference is we have added a Azure Automation using Hybrid Runbook Worker that could run the SharePoint on-premises site creation. The previous Azure function will call the Azure Automation webhook. 

The key part here is to configure the Azure automation Runbook like PowerShell through Hybrid Runbook Worker. This way the PowerShell from Azure automation can provisioning the on-premises site through on-premises site creation method. Here is the architecture of the Azure automation Runbook Worker.


If you have set up Azure automation Runbook Worker, everything is almost same as we configured previous for SharePoint online site provisioning process. The overall architecture diagram is listed below.



  1. User enters site provisioning information from SharePoint Framework (SPFx) form on SPO site.
  2. The new list item created will trigger the Azure Logic app to send to approval.
  3. If the request approved, Azure Logic App will send the request to Azure queue with request ID that is list item ID. We have separate the two queues and if the request of for on-premises site creation, the request will be send to on-premises queue.
  4. Azure queue will trigger the Azure function.
  5. Azure function will be trigger by new message from the queue.
  6. Azure function will call the Azure Automation through webhook. Azure automation will use Runbook worker to provision the site collection using on-premises web services. Then perform post provisioning steps.
  7. Azure Automation will update the SPO site request list with correct status.
  8. Logic app will read the SPO site request list status
  9. Logic app will send the emaul notification on the site creation.
Since this process is almost identical to SharePoint online site provisioning process except the step 6 & 7 Azure function will call Azure automation. You could refer previous blog for other steps.

The step 6 is Azure function will call the Azure Automation through webhook. You could set up a Azure automation Runbook Webhook as described procedure here. The Azure automation Runbook will look likes as below. Please remember to select the Hybrid Worker for "Run On". You need to record the webhook URL after created and you may not able to get it later!


The you could call this Azure automation just like the call below.

#Import dll from library
Import-Module "D:\home\site\wwwroot\qcsbxssspqueseprocessPS\Modules\SharePointPnPPowerShellOnline\3.0.1808.1\SharePointPnPPowerShellOnline.psd1" -Global;
#Get trigger content
$requestBody = Get-Content $triggerInput -Raw | ConvertFrom-Json
#$itemID = $requestBody.ItemId
$siteTitle = $requestBody.SiteTitle
$siteURL = $requestBody.SiteUrl
$output_SiteTitle = "SITETITLE: " + $siteTitle
Write-Output $output_SiteTitle
$output_SiteUrl = "SITEURL: " + $siteURL
Write-Output $output_SiteUrl

try
{
#region constructing Webhook call body
$webhookurl = 'https://s2events.azure-automation.net/webhooks?token=<webhookToken>'
$body = @{"SITETITLE" = $siteTitle; "SITEURL" = $siteURL}
$params = @{
ContentType = 'application/json'
Headers = @{'from' = 'Harry Chen'; 'Date' = "$(Get-Date)"}
Body = ($body | convertto-json)
Method = 'Post'
URI = $webhookurl
}
#Invoking call
Invoke-RestMethod @params -Verbose
Write-Output "Call Invoked - awaiting updating list item"
# Todo: Waite and query the SharePoint
#Connect-PnPOnline -url $requestListSiteUrl -Credentials $creds
#Set-PnPListItem -List $requestList -Identity $itemID -Values @{"Provisioning_x0020_Status" = "Provisioned";}
Write-Output "Item Updated"

#end region

}
catch [System.Exception]
{
$output = "Error Details: "  + $_.Exception.Message
Write-Output $output

}


The step 7 is to use web service to create on-premises site. We tried SP App model in on-premises with just CSOM mentioned by Vesa Juvonen. However, we are running into some issues. As a result, we are using the on-premises admin web service "_vti_adm/Admin.asmx?WSD" to create the on-premises site.
The Azure automation code looks likes as below.

......

[CmdletBinding()]
Param(
[object]$WebhookData,
[string]$siteTitle,
[string]$siteUrl)
if ($WebhookData)
{
Write-Output ("Starting runbook from webhook")
# Collect properties of WebhookData
$WebhookName = $WebHookData.WebhookName
$WebhookHeaders = $WebHookData.RequestHeader
$WebhookBody = $WebHookData.RequestBody

# Collect individual headers. Input converted from JSON.
$From = $WebhookHeaders.From
$InputBody = (ConvertFrom-Json -InputObject $WebhookBody)
Write-Verbose "WebhookBody: $InputBody"

$url = $InputBody.url
$fileName = $InputBody.fileName
Write-Output -InputObject ('Runbook started from webhook {0} by {1}.' -f $WebhookName, $From)
$siteTitle = $InputBody.siteTitle
$output = "Site Title is: " + $siteTitle
Write-Output $output
$siteUrl = $InputBody.siteUrl
$output = "Site Url is: " + $siteUrl
Write-Output $output
} else
{
Write-Output ("Starting runbook manually")
Write-Output ("Input Parameters following:")
$output = "Site Title is: " + $siteTitle
Write-Output $output
$output = "Site Url is: " + $siteUrl
Write-Output $output
}

try
{

$user = "owner"# Owner Login
$userName = "owner"# Owner Name
$pwd = 'password'
$adminSiteUrl = "http://SPadmin:port#/"
$securePwd = ConvertTo-SecureString $pwd -AsPlainText -Force
$cred = New-Object PSCredential($user, $securePwd)
$wsdlUrl = $adminSiteUrl + "/_vti_adm/Admin.asmx?WSDL"
$svc = New-WebServiceProxy -Uri $wsdlUrl -Credential $cred
$output = "Before Running Web Service - Site URL:" + $siteUrl + " Site Title: " + $siteTitle
write-output $output
$svc.CreateSite(
$siteUrl, # URL
$siteTitle, # Title
"", # Description
1033, # LCID
"STS#0", # WebTemplate
$user, # Owner Login
$userName, # Owner Name
"", # Owner Email
"", # PortalUrl
"") # PortalName

# Todo: Udpate the SPO list status w/ Completed status
# Post provisioning steps
}
catch
{
write-Output "`n[Main] Errors found:`n$_"
# Todo: Udpate the SPO list status w/ error status
}


You can see this is remote site provisioning process and the Azure Runbook worker can be any on-premises
server and does not need to be the SharePoint server. We have verified that the admin web service is supported
for SharePoint 2013, 2016, and 2019. So the code is same for all SharePoint versions.


At this point, we are using SharePoint farm account to provisioning the on-premises site. We are testing if
we could use another account to create site.

Now, we have combined both SharePoint online and SharePoint on-premises site creation into a same Azure
process!

Tuesday, November 28, 2017

The "disaster" of SharePoint Server 2016 MinRole

MinRole is a new farm topology based on a set of predefined server roles introduced in SharePoint Server 2016. When configuring your SharePoint farm, you now select the role of a server when you create a new farm or join a server to an existing farm. SharePoint will automatically configure the services on each server based on the server's role. SharePoint Server 2016 has been optimized for the MinRole farm topology.

After we followed the Microsoft instructions to configure the SharePoint 2016 servers as Front-end with Distributed Cache, Application, Application with Search, we found one issue. All wsp solutions deployed to ALL the servers including WFE, search and app servers!



After looking at the server role in details, we found “Microsoft SharePoint Foundation Web Application” service enabled as default for “Application” role and “Application with Search” role. This will cause few major issues but not limited as listed below.


1. Third party license issues. Most of the third party solutions are licensed by WFEs that is defined that server is running “Microsoft SharePoint Foundation Web Application” service. You may run into license issues if all servers have the service running.

2. Deployment performance issue. The custom solution will take significant time since it will deploy to all servers. We had this issue for one small solution deployment.

3. Potential security issues. The application servers or the search servers can also accept the call event they may not intended to server the end users request. This may cause security concerns since most WFE are behind load balancer.

4. Maintainability issues. The application server and search server will have all files related to custom solution deployed. It’s difficult to manage the configurations.

We have seen people to create custom role to avoid this issue. 

It looks like a "disaster" to include the “Microsoft SharePoint Foundation Web Application” service in the “Application” MinRole.

We have discussed with Microsoft to move the following two services to WFE so we have two custom MinRoles for WFE and App servers. If you need to migrate the default MinRole to custom MinRole, you could refer following two articles.


Thursday, November 2, 2017

How to workaround "Sorry, we are having trouble connecting to the server" error from Chrome and Edge for SharePoint 2016

After configured SharePoint 2016 with FP2, we have seen  "Sorry, we are having trouble connecting to the server" error in many places using Chrome and Edge. This error does not happened when you are using Firefox or IE.  Here are the findings and workaround.

1. Here are some functions but not limited to you will receive this error. 
  • Add users to any permission groups like administrator, the error displayed when you typing the user but could not resolve the name. (See picture #1)
  • Unable to add any list item to the list (See picture #2)
  • Unable to delete any list item from list view
  • Unable to share documents with anyone
  • Unable to add list template to list template gallery
  • Unable to delete list template from list template gallery
  • Constantly getting error "Sorry, we are having trouble connecting to the server".



2. Debugging error from fiddler. 

If you debug the issue through Fiddler, you will find the “403” error and the “Origin” under Security of the request Header is dropping the port number like this below.
“Origin: myprojectsite.mycompany.com”

3. Steps to reproduce the issues.

We did further debugging with Microsoft and identified this is new security “feature” implemented in SharePoint 2016 that is causing this issue. This is impacting any web applications with SSL that is not in default 443 port number. Here is the step to reproduce the issue.

In SharePoint 2016 create two web applications with root site collections and then enable for SSL
One web applications use default port number and another is something like 51000.
In central admin go to  alternate access mapping settings and add internal URL mapping to the second new web application.  Use the same name and append a port number.
Example map:

Internal URL
Zone
Public URL
https:\\myprojectsite.mycompany.com
Default
https:\\ myprojectsite.mycompany.com
https:\\myprojectsite.mycompany.com:51000
Default
https:\\ myprojectsite.mycompany.com

Go to IIS on the WFE and edit binding, change 443 to port 51000 and apply SSL cert to binding.
On load balance device configure for port redirection and SSL offload
Configure device to listen for https:\\myprojectsite.mycompany.com
Configure device to send traffic to WFE node as https:\\myprojectsite.mycompany.com:51000
Browse to the site as https:\\myprojectsite.mycompany.com
Site settings -> People and Groups ->New ->Add user, people picker should be present now. Type a user name and press to activate name resolution.
Error message "Sorry, we are having trouble connecting to the server" will be displayed

We have tied to add a Custom Rule in the fiddler like below and the issue can be resolved.
    static function OnBeforeRequest(oSession: Session) {
        if ( oSession.HostnameIs("myprojectsite.mycompany.com") && oSession.uriContains("/ProcessQuery")) {
            oSession["ui-bold"]="true";
            oSession.oRequest["Origin"]="https://myprojectsite.qualcomm.com:51000";
        }
       // …
 }

4. Multiple options to work around this issue.

Now we have few options to work around this issue. Here are the options confirmed with Microsoft.
  • Option 1 - Create a rule in the Load balancer
  • Option 2 - Use the same SSL certificate on all the web applications in the farm using a SAN configuration and configure all the web applications to use port 443 and a host header
  • Option 3 - Configure all the VIPs in the LB to forward to the SharePoint servers on port 443 instead of the port the web applications is actually listening on
  • Option 4 - Configure the SharePoint servers to have multiple IP addresses for each web applications so they all can use port 443.


We have implemented the option one by adding the following rule to the Load balancer.
IF the hostname = "myprojectsite.mycompany.com " && the URI contains "/ProcessQuery”

You might try other options that should also resolve the issue.