Skip to main content

Walkthrough: Deploying Sitecore Connect for Content Hub in an existing PaaS environment

Abstract

How to deploy the Sitecore Connect for Content Hub connector in an existing Azure PaaS environment.

You can deploy Sitecore Connect for Content Hub (SCCH) in an existing Azure PaaS Environment.

To deploy in an existing PaaS environment, you must have an existing Sitecore XM or XP Azure PaaS Environment andMicrosoft Web Deploy installed.

Note

If you are deploying a new Sitecore Azure PaaS environment, use the instructions in Deploy Sitecore Connect for Content Hub in a new PaaS environment instead.

This walkthrough describes how to:

  • Prepare an installation folder

  • Prepare the script input

  • Install and configure the connector

  • Troubleshoot installation errors

To prepare an installation folder:

  1. Create a local folder in your file system, for example, C:\Temp\SCCHInstallation.

  2. Download the Sitecore Connect for Content Hub WDP Package from the Sitecore download page and store it in the local folder you created.

  3. In the local folder, create a new file and name it Deploy.ps1.

  4. Open the new file with an editor such as notepad or VS Code and paste the following script inside:

    [CmdletBinding(DefaultParameterSetName = "no-arguments")]
    param(
        [Parameter(HelpMessage = "Name of the resource group in Azure to target.")]
        [string]$ResourceGroupName,
    
        [Parameter(HelpMessage = "Name of the web app in Azure to target.")]
        [string]$WebAppName,
    
        [Parameter(HelpMessage = "Path to the WDP to deploy to the target.")]
        [string]$WdpPackagePath,
    
        [Parameter(HelpMessage = "Content Hub Client Id.")]
        [string]$CHClientId,
    
        [Parameter(HelpMessage = "Content Hub Client Secret.")]
        [string]$CHClientSecret,
    
        [Parameter(HelpMessage = "Content Hub Username.")]
        [string]$CHUserName,
    
        [Parameter(HelpMessage = "Content Hub Password.")]
        [string]$CHPassword,
    
        [Parameter(HelpMessage = "Content Hub URI.")]
        [string]$CHUri,
    
        [Parameter(HelpMessage = "Content Hub Azure Service Bus connection string path in.")]
        [string]$CHServiceBusEntityPathIn,
    
        [Parameter(HelpMessage = "Content Hub Subscription name. (must be unique per Sitecore CM deployment)")]
        [string]$CHServiceBusSubscription,
    
        [Parameter(HelpMessage = "Content Hub Azure Service Bus connection string path out.")]
        [string]$CHServiceBusEntityPathOut,
    
        [Parameter(HelpMessage = "Content Hub Search Page Uri.")]
        [string]$CHSearchPage,
    
        [Parameter(HelpMessage = "Content Hub External Redirect Key.")]
        [string]$CHExternalRedirectKey = "Sitecore",
    
        [Parameter(HelpMessage = "Path to MSDeploy.")]
        [string]$MsDeployPath = "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe",
    
        [Parameter(HelpMessage = "Skips Azure Login when True.")]
        [switch]$SkipAzureLogin = $False,
    
        [Parameter(HelpMessage = "Amount of retry attempts. 6 by default which with default retryinterval would come down to 1 minute.")]
        [int]$RetryAttempts = 6,
    
        [Parameter(HelpMessage = "Amount of time to wait between retries in milliseconds. 10000 by default which is 10 seconds which adds up to 1 minute with default retry attempts.")]
        [int]$RetryInterval = 10000
    )
    
    Add-Type -AssemblyName "System.IO.Compression.FileSystem"
    
    function PreparePath($path) {
        if(-Not (Test-Path $path)) {
            $result = New-Item -Path $path -Type Directory -Force
        } else {
            $result = Resolve-Path $path
        }
    
        return $result
    }
    
    function UnzipFolder($zipfile, $folder, $dst) {
        [IO.Compression.ZipFile]::OpenRead($zipfile).Entries | Where-Object {
            ($_.FullName -like "$folder/*") -and ($_.Length -gt 0)
        } | ForEach-Object {
            $parent = Split-Path ($_.FullName -replace $folder, '')
            $parent = PreparePath (Join-Path $dst $parent)
            $file = Join-Path $parent $_.Name
            [IO.Compression.ZipFileExtensions]::ExtractToFile($_, $file, $true)
        }
    }
    
    function DownloadWebsiteFile($filePath, $downloadFolderName) {
        $basePath = Split-Path ".\$downloadFolderName\$filePath"
        $fileName = Split-Path $filePath -Leaf
        if(-Not (Test-Path ".\$downloadFolderName\$filePath")) {
            New-Item -Path $basePath -Type Directory -Force
        }
        $outFilePath = Join-Path (Resolve-Path "$basePath") $fileName
        Invoke-WebRequest -Uri "https://$WebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/$filePath" -Headers @{"Authorization"=("Basic {0}" -f $base64AuthInfo)} -Method GET -OutFile $outFilePath
    }
    
    function UploadWebsiteFile($filePath, $uploadFilePath) {
        Invoke-WebRequest -Uri "https://$WebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/$filePath" -Headers @{"Authorization"=("Basic {0}" -f $base64AuthInfo);"If-Match"="*"} -Method PUT -InFile $uploadFilePath
    }
    
    function ApplyTransform($filePath, $xdtFilePath) {
        Write-Verbose "Applying XDT transformation '$xdtFilePath' on '$filePath'..."
    
        $target = New-Object Microsoft.Web.XmlTransform.XmlTransformableDocument;
        $target.PreserveWhitespace = $true
        $target.Load($filePath);
        
        $transformation = New-Object Microsoft.Web.XmlTransform.XmlTransformation($xdtFilePath);
        
        if ($transformation.Apply($target) -eq $false)
        {
            throw "XDT transformation failed."
        }
        
        $target.Save($filePath);
    }
    
    if(-Not (Test-Path $MsDeployPath)) {
        Write-Host "MS Deploy was not found at `"$MsDeployPath`"!" -ForegroundColor Red
        return
    }
    
    if(-Not $SkipAzureLogin) {
        Write-Host "Logging into Azure..." -ForegroundColor Green
        & az login
    }
    
    
    Write-Host "Fetching Publish Profile..." -ForegroundColor Green
    $publishProfile = az webapp deployment list-publishing-profiles --resource-group $ResourceGroupName --name $WebAppName --query "[?publishMethod=='MSDeploy']" | ConvertFrom-Json
    $userName = $publishProfile.userName
    $password = $publishProfile.userPWD
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userName, $password)))
    
    
    Write-Host "Preparing configuration..." -ForegroundColor Green
    $xdtsPath = (PreparePath ".\xdts")
    UnzipFolder $WdpPackagePath "Content/Website/App_Data/Transforms/scch/xdts" $xdtsPath
    Get-ChildItem $xdtsPath -File -Include "*.xdt" -Recurse | ForEach-Object {
        $targetWebsiteFile = $_.FullName.Replace("$xdtsPath\", "").Replace("\", "/").Replace(".xdt", "")
        DownloadWebsiteFile $targetWebsiteFile "Configuration"
    }
    $configurationPath = (PreparePath ".\Configuration")
    $currentDateTime = (Get-Date).ToString("dd-MM-yyyy-hh-mm-ss")
    $backupPath = (PreparePath ".\Backup-$currentDateTime")
    robocopy $configurationPath $backupPath /s
    
    
    Write-Host "Preparing transformations..." -ForegroundColor Green
    $nupkgPath = Join-Path (Resolve-Path ".") "microsoft.web.xdt.3.1.0.nupkg"
    $xdtDllBinPath = PreparePath ".\bin"
    Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/Microsoft.Web.Xdt/3.1.0" -OutFile $nupkgPath
    UnzipFolder $nupkgPath "lib/netstandard2.0" $xdtDllBinPath
    Add-Type -Path (Resolve-Path ".\bin\Microsoft.Web.XmlTransform.dll")
    
    
    Write-Host "Fill ConnectionStrings..." -ForegroundColor Green
    $connectionStringsXdtPath = Join-Path $xdtsPath "App_Config\ConnectionStrings.config.xdt"
    ((Get-Content -Path $connectionStringsXdtPath -Raw).Replace("{client_id}", $CHClientId).Replace("{client_secret}", $CHClientSecret).Replace("{username}", $CHUserName).Replace("{password}", $CHPassword).Replace("{uri}", $CHUri).Replace("{Azure Service Bus connection string with incoming topic}", $CHServiceBusEntityPathIn).Replace("{Subscription name}", $CHServiceBusSubscription).Replace("{Azure Service Bus connection string with outcoming topic}", $CHServiceBusEntityPathOut).Replace("{Content Hub search page URI}", $CHSearchPage).Replace("{External redirect key}", $CHExternalRedirectKey)) | Set-Content -Path $connectionStringsXdtPath
    
    
    Write-Host "Running transformations..." -ForegroundColor Green
    Get-ChildItem $xdtsPath -File -Include "*.xdt" -Recurse | ForEach-Object {
        $targetFilePath = $_.FullName.Replace($xdtsPath, $configurationPath).Replace(".xdt", "")
        if (-not(Test-Path $targetFilePath -PathType Leaf)) {
            Write-Verbose "No matching file '$targetFilePath' for transformation '$($_.FullName)'. Skipping..."
        } else {
            ApplyTransform $targetFilePath $_.FullName
        }
    }
    
    
    Write-Host "Starting MSDeploy..." -ForegroundColor Green
    $verb = "-verb:sync"
    $source = "-source:package=`"$WdpPackagePath`""
    $dest = "-dest:auto,ComputerName=`"https://$WebAppName.scm.azurewebsites.net/msdeploy.axd?site=$WebAppName`",UserName=`"$userName`",Password=`"$password`",AuthType=`"Basic`""
    $iisWebAppParam = "-setParam:name=`"IIS Web Application Name`",value=`"$WebAppName`""
    $coreParam = "-setParam:name=`"Core Admin Connection String`",value=`"notUsed`""
    $masterParam = "-setParam:name=`"Master Admin Connection String`",value=`"notUsed`""
    $skipDbFullSql = "-skip:objectName=dbFullSql"
    $skipDbDacFx = "-skip:objectName=dbDacFx"
    $doNotDeleteRule = "-enableRule:DoNotDeleteRule"
    $appOfflineRule = "-enableRule:AppOffline"
    $retryAttemptsParam = "-retryAttempts:$RetryAttempts"
    $retryIntervalParam = "-retryInterval:$RetryInterval"
    $verboseParam = "-verbose"
    Invoke-Expression "& '$MsDeployPath' --% $verb $source $dest $iisWebAppParam $coreParam $masterParam $skipDbFullSql $skipDbDacFx $doNotDeleteRule $appOfflineRule $retryAttemptsParam $retryIntervalParam $verboseParam"
    
    
    Write-Host "Uploading configuration..." -ForegroundColor Green
    Get-ChildItem $configurationPath -File -Recurse | ForEach-Object {
        $targetWebsiteFile = $_.FullName.Replace("$configurationPath\", "").Replace("\", "/")
        UploadWebsiteFile $targetWebsiteFile $_.FullName
    }
    
  5. If you are installing SCCH on Sitecore Experience Platform (SXP) 10.1 or earlier, using DACPAC, remove the $skipDbFullSql and $skipDbDacFx parameters in the script. The script line will look like this:

    Invoke-Expression "& '$MsDeployPath' --% $verb $source $dest $iisWebAppParam $coreParam $masterParam $doNotDeleteRule $appOfflineRule $retryAttemptsParam $retryIntervalParam $verboseParam"

    After you remove the parameters, update the connection strings for the Core and Master admin parameters:

    $coreParam = "-setParam:name=`"Core Admin Connection String`",value=`"<core connection string>`""
    $masterParam = "-setParam:name=`"Master Admin Connection String`",value=`"<master connection string>`"

    Note

    You can find the connections string by navigating to your CM instance. For example:

    https://<webappname>.scm.azurewebsites.net/dev/wwwroot/App_Config/ConnectionStrings.config

    The connection string must contain the MS SQL admin credentials in the User Id and Password parameters.

  6. Save and close the file.

To successfully run the script and install and configure the connector, you must prepare the following parameters:

  • ResourceGroupName - the resource group name in Azure you want to install to.

  • WebAppName - the name of the Web App in Azure you want to install to.

  • CHClientId and CHClientSecret - a Content Hub OAuth Client ID and Client Secret. (See Create an OAuth client for how to create these).

  • CHUserName and CHPassword - a Content Hub username and password that is used as the identification of Sitecore to access Content Hub.

  • CHUri - the URI to your Content Hub instance, for example, https://mysandbox.stylelabs.io/.

  • CHServiceBusEntityPathIn and CHServiceBusEntityPathOut - to find these connection strings, in Content Hub, create a new action of the type M Azure Service Bus. Make a note of the connection strings in Hub in and Hub out. For example:

    The Edit action dialog box showing the Hub in and Hub out connection strings
  • CHServiceBusSubscription - the name of your Sitecore subscription.

  • CHSearchPage - the URI to the Search Page you want to use to select DAM Assets, for example, https://mysandbox.stylelabs.io/en-us/sitecore-dam-connect/approved-assets.

To run the installation of the connector and apply the configuration:

  1. Open a PowerShell window with administrator access.

  2. Navigate to your local folder. For example:

    cd "C:\Temp\SCCHInstallation"
  3. Run the following command:

    az account set –subscription “<subscription name or id>”
  4. Run the following command with the parameters you have prepared:

    .\Deploy.ps1 -ResourceGroupName "<MyResourceGroup>" -WebAppName "<MyWebApp>" -WdpPackagePath "C:\Temp\SCCHInstallation\Sitecore.Connector.ContentHub.WDP.5.0.0-r00328.4145.scwdp.zip" -CHClientId "<ClientId>" -CHClientSecret "<ClientSecret>" -CHUserName "<UserName>" -CHPassword "<Password>" -CHUri "https://mysandbox.stylelabs.io/" -CHServiceBusEntityPathIn "<Hub out connectionstring>" -CHServiceBusSubscription "<MySitecoreSubscription>" -CHServiceBusEntityPathOut "<Hub in connectionstring>" -CHSearchPage "https://mysandbox.stylelabs.io/en-us/sitecore-dam-connect/approved-assets"

    Note

    You must run this command against both your CM and your CD Azure PaaS Web Applications.

  5. If you use the DAM functionality in SCCH, to allow the connector to select assets from Content Hub, add all hostnames to the Content-Security-Policy tag.

You might encounter the following error(s) when installing SCCH in an existing Azure PaaS environment.

Error: Could not load file or assembly ‘System.Runtime.CompilerService.Unsafe’

This error can occur when you have configured an Azure redis cache. If you get a version conflict error for System.Runtime.CompilerService.Unsafe version 4.0.4.1, add the following node to your webconfig file:

<dependentAssembly>
    <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" culture="neutral"/>
    <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.0"/>
</dependentAssembly>