[TechSpeak] How to Package Your Local Sitecore Instance for Microsoft Azure App Service

Updated Apr 8, 2024

For modern businesses, it’s extremely important to be flexible in order to satisfy the constantly changing customer needs. That’s why more and more companies turn to the evolving cloud computing services, which provide scalability, optimize connectivity and improve performance.

Sitecore is not an exception, as it allows you to deploy solutions on the Azure Platform as a Service (PaaS). For a new Sitecore environment, you can use out-of-the-box web deploy packages (WDP). But to deploy your existing (local) solution, you need to initially package it into a WDP.

In this article, I will tell you how you can package your local Sitecore Habitat into web deploy package (WDP). You will also see what issues you might run into during the process.

Guideline

Let’s imagine that you have a local Sitecore instance (in my case Sitecore Habitat) and you would like to deploy this instance into Microsoft Azure as PaaS.

For this purpose, you can use a special tool — Sitecore Azure Toolkit.

Main Sitecore Azure Toolkit features:

  • Packaging of Sitecore instances into role-specific packages using PowerShell command line programs
  • Integration with Microsoft Azure services
  • ARM templates for topologies such as XM, XP, XPSingle
  • Configuration for running on the Azure App Service
  • Security features provided by HTTPS, low-privileged SQL access, Sitecore password

Download Sitecore Azure Toolkit current version 2.2 here.

Step 1

To start working on this tool, you just need to extract the downloaded archive to a folder. In my case “C:\Sitecore\Azure Toolkit”.

Also, don’t forget to check that you have all the required prerequisites:

  • A cloud-hosted MongoDB cluster for the xDB Collection and Tracking databases that are used with XP and XP0. Note: this is a prerequisite for versions earlier than 9.0 only.
  • .NET Framework 4.6
  • PowerShell 4.0
  • Microsoft Azure PowerShell 2.0.1 or later.
  • Microsoft SQL Server Data-Tier Application Framework (DacFX) for SQL server 2012 or later. Note: this framework is usually installed with SQL Server or Visual Studio.

Step 2

Then, open the root folder where the toolkit is unpacked and load the main module into a PowerShell terminal :

Import-Module .\ tools \ Sitecore . Cloud.Cmdlets.psm1 -Verbose

This module has some commandlets responsible for:

  1. Packaging a module into WDP
  2. Packaging a local instance into WDP
  3. Deployment onto Azure

Our main goal is to make a Sitecore web deploy package ( SCWDP ) based on local Sitecore Habitat instance. Run the following commandlet to achieve this goal:

Start-SitecoreAzurePackaging

This command contains parameters required for executing:

  • SitecorePath — the path to either a folder where the Sitecore installation should be packaged or to a folder in a zip file.
  • DestinationFolderPath — the path to the folder for generated packages storage.
  • CargoPayloadFolderPath — the path to the folder with the version-specific role and feature transformation files.
  • CommonConfigPath — the path to the file with a list of the transformation files to be applied to all of the roles.
  • SkuConfigPath — the path to the file with lists of role-specific transformations for the selected version and the Sitecore configuration on the Microsoft App Service.
  • ParameterXmlPath — the path to the WebDeploy Sitecore version-specific archive manifest and parameter declaration files.
  • FileVersion — an optional parameter for embedding a version marker in the generated WebDeploy packages.

I will note one more optional parameter a bit later.

Step 3

You should have your local instance on your dev machine. On my local machine, I have a Habitat Sitecore instance.

Local-Habitat-Sitecore-instance-photo

The website is located in C:\inetpub\wwwroot\habitat.dev.local and published on IIS as habitat.dev.local.

Habitat-local-website-location-photo

As for my local habitat SQL databases — they all are located in another VM machine.

Step 4

Now, you have to define the destination folder path. I would like to save the generated Sitecore WDP package into C:\Sitecore\WDPs.

I’ve already mentioned that the Azure toolkit is unpacked into:

Sitecore-Azure-Toolkit-local-photo

Let’s populate all the parameters we need to run with Start-SitecoreAzurePackaging command:

1$sitecorePath = "C:\inetpub\wwwroot\habitat.dev.local"
2$destinationFolderPath = "C:\Sitecore\WDPs"
3$cargoPayloadFolderPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\CargoPayloads"
4$commonConfigPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\Configs\common.packaging.config.json"
5$skuConfigPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\Configs\XPSingle.packaging.config.json"
6$parameterXmlPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\MsDeployXmls"
7$fileVersion = "9.1.1"

And define these parameters with a command:

1Start-SitecoreAzurePackaging -sitecorePath $sitecorePath -destinationFolderPath $destinationFolderPath `
2-cargoPayloadFolderPath $cargoPayloadFolderPath -commonConfigPath $commonConfigPath `
3-skuConfigPath $skuConfigPath -parameterXmlPath $parameterXmlPath -fileVersion $fileVersion

Below, you can see how it looks like in my PowerShell ISE terminal session.

terminal-session-PowerShell-ISE-photo

Step 5

Finally, it seems that we are ready to click on the green button to run Sitecore Azure Packaging!

But take your time! I instantly caught an error:

Local-sitecore-habitat-Issue-SQL-photo

The following is the error text:

1Connecting to database 'habitat_Core' on server 'test-server'.
2Extracting schema
3Extracting schema from database
4New-SCWebDeployPackage : Connecting to database 'habitat_Core' on server 'test-server'.
5Extracting schema
6Extracting schema from database
7At C:\Sitecore\Azure Toolkit\tools\Sitecore.Cloud.Cmdlets.psm1:195 char:49
8+ ... ckagePath = New-SCWebDeployPackage -Path $SitecorePath -Destination $ ...
9+              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10+ CategoryInfo       : NotSpecified: (Connecting to d...a from database:String) [New-SCWebDeployPackage], Exception
11+ FullyQualifiedErrorId : Microsoft.Extensions.Logging.EventId,Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage
12System.IO.FileNotFoundException: Could not find file 'C:\Users\vap\AppData\Local\Temp\tempDacPacs1d5912bf-c65a-4c53-8f70-e8a335d6ea37\Sitec
13ore.core.dacpac'.
14File name: 'C:\Users\vap\AppData\Local\Temp\tempDacPacs1d5912bf-c65a-4c53-8f70-e8a335d6ea37\Sitecore.core.dacpac'
15   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
16   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 buff
17erSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
18   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, Str
19ing msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
20   at System.IO.File.InternalReadAllBytes(String path, Boolean checkHost)
21   at Sitecore.Cloud.Package.Common.FileSystemProvider.ReadFile(String fileName)
22   at Sitecore.Cloud.Packaging.WebDeployPackages.DatabasePackager.PackageDatabases(XDocument connStringFile, Boolean integratedSecurity)
23   at Sitecore.Cloud.Packaging.WebDeployPackages.WebDeployPackageBuilder.Build(SitecoreInstallationFolderTree scInstFoldTree, DirPath outpu
24tDir, String targetFileName, Boolean force, String version, Boolean integratedSecurity)
25   at Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage.ProcessRecord()
26New-SCWebDeployPackage : System.IO.FileNotFoundException: Could not find file 'C:\Users\vap\AppData\Local\Temp\tempDacPacs1d5912bf-c65a-4c
2753-8f70-e8a335d6ea37\Sitecore.core.dacpac'.
28File name: 'C:\Users\vap\AppData\Local\Temp\tempDacPacs1d5912bf-c65a-4c53-8f70-e8a335d6ea37\Sitecore.core.dacpac'
29   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
30   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 buf
31ferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
32   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, St
33ring msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
34   at System.IO.File.InternalReadAllBytes(String path, Boolean checkHost)
35   at Sitecore.Cloud.Package.Common.FileSystemProvider.ReadFile(String fileName)
36   at Sitecore.Cloud.Packaging.WebDeployPackages.DatabasePackager.PackageDatabases(XDocument connStringFile, Boolean integratedSecurity)
37   at Sitecore.Cloud.Packaging.WebDeployPackages.WebDeployPackageBuilder.Build(SitecoreInstallationFolderTree scInstFoldTree, DirPath outp
38utDir, String targetFileName, Boolean force, String version, Boolean integratedSecurity)
39   at Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage.ProcessRecord()
40At C:\Sitecore\Azure Toolkit\tools\Sitecore.Cloud.Cmdlets.psm1:195 char:49
41+ ... ckagePath = New-SCWebDeployPackage -Path $SitecorePath -Destination $ ...
42+              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
43+ CategoryInfo       : NotSpecified: (System.IO.FileN...ProcessRecord():String) [New-SCWebDeployPackage], Exception
44+ FullyQualifiedErrorId : Microsoft.Extensions.Logging.EventId,Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage
45Could not find file 'C:\Users\vap\AppData\Local\Temp\tempDacPacs1d5912bf-c65a-4c53-8f70-e8a335d6ea37\Sitecore.core.dacpac'.

Hmm. Could not find the generated *.dacpac file in a temp folder. I’ve just checked this folder and found nothing. What could be a reason?

In a stack trace, I see that this happened during “ Extracting schema from database.

As I’ve mentioned before, all habitat.dev.local SQL dbs are located on another VM machine. That’s probably why the “sqlpackage.exe” (Microsoft SQL Server command-line tool used by Sitecore Azure toolkit to create a Sitecore database snapshot *.dacpac file from a live SQL Server) was not able to either connect SQL or generate the appropriate *.dacpac file.

I started “dotPeeking” (decompiling) Azure Toolkit binaries responsible for Start-SitecoreAzurePackaging to find the place where sqlpackage.exe joined the game. These are the most involved ones:

Sitecore-commandlets-dotPeek-Binaries-photo

In a while, I figured out a method

Sitecore.Cloud.Packaging.WebDeployPackages.CreateDacPac(string dbConnectionString, string targetFilePath){…}

in the source code where the appropriate dacpac file is being generated.

To test this functionality, I created a simple Console application to check if sqlpackage.exe works as expected. When I executed this app, I got the following output log:

1Connecting to database 'habitat_Core' on server '<sql server name>'.
2Extracting schema
3Extracting schema from database
4*** Error extracting database:Could not extract package from specified database.
5The reverse engineering operation cannot continue because you do not have View Definition permission on the 'habitat_Core' database.

Aha, I see that a user defined in connection strings (connections that go from the App_Config/ConnectionStrings.config file in your instance) do not have appropriate grant permissions to be able to make a Sitecore.core.dacpac file.

So, I went to SQL server and noticed that “ coreuser” (this user is defined in a “core” connection string) does not belong to db_owner role:

Sitecore-habitat-SQL-DB-Owner-photo

I applied this role, rerun my Console App and got a Success output message:

1Connecting to database 'habitat_Core' on server 'test-server'.
2Extracting schema
3Extracting schema from database
4Resolving references in schema model
5Validating schema model for data package
6Validating schema
7Exporting data from database
8Exporting data
9Processing Export.
10Processing Table '[dbo].[AccessControl]'.
11Processing Table '[dbo].[Archive]'.
12Processing Table '[dbo].[ArchivedFields]'.
13Processing Table '[dbo].[ArchivedItems]'.
14Processing Table '[dbo].[ArchivedVersions]'.
15Processing Table '[dbo].[aspnet_Applications]'.
16Processing Table '[dbo].[aspnet_Membership]'.
17Processing Table '[dbo].[aspnet_Paths]'.
18Processing Table '[dbo].[aspnet_PersonalizationAllUsers]'.
19Processing Table '[dbo].[aspnet_PersonalizationPerUser]'.
20Processing Table '[dbo].[aspnet_Profile]'.
21Processing Table '[dbo].[aspnet_Roles]'.
22Processing Table '[dbo].[aspnet_SchemaVersions]'.
23Processing Table '[dbo].[aspnet_Users]'.
24Processing Table '[dbo].[aspnet_UsersInRoles]'.
25Processing Table '[dbo].[aspnet_WebEvent_Events]'.
26Processing Table '[dbo].[Blobs]'.
27Processing Table '[dbo].[ClientData]'.
28Processing Table '[dbo].[Descendants]'.
29Processing Table '[dbo].[EventQueue]'.
30Processing Table '[dbo].[History]'.
31Processing Table '[dbo].[IDTable]'.
32Processing Table '[dbo].[Items]'.
33Processing Table '[dbo].[Links]'.
34Processing Table '[dbo].[Notifications]'.
35Processing Table '[dbo].[Properties]'.
36Processing Table '[dbo].[PublishQueue]'.
37Processing Table '[dbo].[RolesInRoles]'.
38Processing Table '[dbo].[SharedFields]'.
39Processing Table '[dbo].[Tasks]'.
40Processing Table '[dbo].[UnversionedFields]'.
41Processing Table '[dbo].[UserLogins]'.
42Processing Table '[dbo].[VersionedFields]'.
43Processing Table '[dbo].[WorkflowHistory]'.
44Successfully extracted database and saved it to file 'C:\Temp\Sitecore.core.dacpac'.
45Success

Now, we can see that the “Sitecore.core.dacpac” is created:

Sitecore-Dacpac-Core-photo

Cool! Then, I applied the “db_owner” role to all users under appropriate dbs defined in:

C:\inetpub\wwwroot\habitat.dev.local\App_Config\ConnectionStrings.config

Note:

At the beginning of the article, I’ve mentioned one optional parameter for executing the command Start-SitecoreAzurePackaging:

  • integratedSecurity
Sitecore-integrated-security-parameter-photo

By default, the value of this parameter is false , which means that the user creds will be used from ConnectionsStrings.config file.

If you specify the value true for this parameter, the process responsible for generating dacpac files will use the current Windows account. But there’s one condition — this account must have administrative rights to access the SQL server.

For some reason, I lost sight of this parameter at the very beginning and, therefore, I had to deal with the problem I described above.

Step 6

Then I executed

1Start-SitecoreAzurePackaging -sitecorePath $sitecorePath -destinationFolderPath $destinationFolderPath `
2-cargoPayloadFolderPath $cargoPayloadFolderPath -commonConfigPath $commonConfigPath `
3-skuConfigPath $skuConfigPath -parameterXmlPath $parameterXmlPath -fileVersion $fileVersion

once again. The issue seems to be gone — the “*.dacpac” files started to appear in a temporary folder, as shown below:

Habitat-packaging-TempDacPacs-photo

In a few minutes, I got another exception message:

Sitecore-Habitat-Permissions-Issue-photo

The error text:

1System.UnauthorizedAccessException: Access to the path 'C:\inetpub\wwwroot\habitat.dev.local\tempConnectionStringeb3ab08c-5c68-4d9d-bf05-8b72f845145c\' is denied.
2   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
3   at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost)
4   at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost)
5   at DotNet.Basics.IO.DirPath.CreateIfNotExists()
6   at DotNet.Basics.IO.FileExtensions.CopyTo(FilePath source, FilePath target, Boolean overwrite, Boolean ensureTargetDir)
7   at Sitecore.Cloud.Packaging.WebDeployPackages.WebDeployPackageBuilder.Build(SitecoreInstallationFolderTree scInstFoldTree, DirPath outputDir, String targetFileName, Boolean force, String version, B
8oolean integratedSecurity)
9   at Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage.ProcessRecord()
10New-SCWebDeployPackage : System.UnauthorizedAccessException: Access to the path 'C:\inetpub\wwwroot\habitat.dev.local\tempConnectionStringeb3ab08c-5c68-4d9d-bf05-8b72f845145c\' is denied.
11   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
12   at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost)
13   at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost)
14   at DotNet.Basics.IO.DirPath.CreateIfNotExists()
15   at DotNet.Basics.IO.FileExtensions.CopyTo(FilePath source, FilePath target, Boolean overwrite, Boolean ensureTargetDir)
16   at Sitecore.Cloud.Packaging.WebDeployPackages.WebDeployPackageBuilder.Build(SitecoreInstallationFolderTree scInstFoldTree, DirPath outputDir, String targetFileName, Boolean force, String version,
17Boolean integratedSecurity)
18   at Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage.ProcessRecord()
19At C:\Sitecore\Azure Toolkit\tools\Sitecore.Cloud.Cmdlets.psm1:195 char:49
20+ ... ckagePath = New-SCWebDeployPackage -Path $SitecorePath -Destination $ ...
21+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22    + CategoryInfo          : NotSpecified: (System.Unauthor...ProcessRecord():String) [New-SCWebDeployPackage], Exception
23    + FullyQualifiedErrorId : Microsoft.Extensions.Logging.EventId,Sitecore.Cloud.Cmdlets.Packaging.NewSCWebDeployPackage
24Access to the path 'C:\inetpub\wwwroot\habitat.dev.local\tempConnectionStringeb3ab08c-5c68-4d9d-bf05-8b72f845145c\' is denied.

The process tries to do some stuff with a temporary file in the root folder of the habitat.dev.local site.

To avoid UnauthorizedAccessException, I just stopped IIS and rerun the PowerShell command.

Success

One minute, two minutes, no errors, waiting…and I see a good message:

Sitecore-habitat-package-success-photo

The goal is achieved — local Habitat Sitecore Web Deploy Package has been generated:

C:\Sitecore\WDPs\habitat.dev.local_single.scwdp.zip

Generated-Sitecore-SCWDP-package-photo

This is an archive where you can see all local Sitecore instance DB dacpac files, base SQL scripts to be executed during future deployment, and parameters:

Generated-SCWDP-local-DB-photo

We can also see a Content directory in which our local Sitecore Habitat root site folder was copied:

Habitat-SCWDP-content-directory-photo

It took 7 minutes for my local Habitat Sitecore instance process to be completed.

Now, we have our local Sitecore instance packed. That means you can deploy this SCWDP to Azure as Sitecore PaaS, but that’s another topic for discussion.

Summary

I’ve described one of the first steps you have to take to prepare your local Sitecore Habitat for Azure deployment — the creation of a Sitecore web deploy package. The following is a summary of the process:

  1. Download the Azure toolkit from the official portal
  2. Unzip the toolkit into your deployment folder, for instance — C:\Sitecore\Azure Toolkit\
  3. Check all prerequisites
  4. Execute the following Commandlets into PowerShell terminal run from the root Azure Toolkit folder:
1Import-Module .\ tools \ Sitecore . Cloud.Cmdlets.psm1 -Verbose
2 
3$sitecorePath = "C:\inetpub\wwwroot\habitat.dev.local"
4$destinationFolderPath = "C:\Sitecore\WDPs"
5$cargoPayloadFolderPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\CargoPayloads"
6$commonConfigPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\Configs\common.packaging.config.json"
7$skuConfigPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\Configs\XPSingle.packaging.config.json"
8$parameterXmlPath = "C:\Sitecore\Azure Toolkit\resources\9.1.1\MsDeployXmls"
9$fileVersion = "9.1.1"
10 
11Start-SitecoreAzurePackaging -sitecorePath $sitecorePath -destinationFolderPath $destinationFolderPath `
12-cargoPayloadFolderPath $cargoPayloadFolderPath -commonConfigPath $commonConfigPath `
13-skuConfigPath $skuConfigPath -parameterXmlPath $parameterXmlPath -fileVersion $fileVersion

If your Windows user has the Administrator role to access SQL server where your local Sitecore instance databases live — just add an additional parameter to avoid some issues I mentioned in this blog post:

-integratedSecurity 1 (means true)

5. Note that in some minutes you have a generated *.scwdp package in a folder you specified in the $destinationFolderPath parameter

6. To be more flexible, I suggest you save your PowerShell script and execute this script using Windows PowerShell ISE tool:

Azure-SCWDP-packaging-success-photo

That’s all for today. If my experience comes in handy for you, I will be happy. If you have any questions, please contact me via Twitter and LinkedIn.

One Comment
Leave a comment
Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>