Deploy Safewhere Identify manually on Azure app service

Deploy Safewhere Identify manually on Azure app service

As-is, because the Configurator tool doesn't support deploying to Azure app service, this document will explore how to do that semi-manually as well as what we need to do next.

The use case is:

  • Deploy an instance on a local machine but use Azure SQL database.
  • Migrate the websites to Azure app service.
  • Because Identify Admin and Runtime are running on .NET framework runtime while Safewhere Admin (adminv2) is using .NET core runtime, you will need to deploy them in two separate app services.
  • In this guide, the domain name of Admin and Runtime will be https://azureapp.safewhere.com/ and that of Safewhere Admin will be https://azureapp-admin.thuannguy.com/.
  • Need to use an Identify build from the branch experiment-66077-azureappservice.
  • No need to purchase a valid SSL certificate because we can use the ones provisioned by Azure.

Creating an Identify instance and preparing a deploying package

You will need to create an instance and do some changes locally:

  1. Create a new instance on a virtual machine as usual named azureapp and run it using NetworkService (using a specific user account locally and a managed identity on Azure app service may be better security-wise).

  2. You need to create the instance using the public domain that you want to use on Azure app service. For example, if you want to make public the site at https://azureapp.safewhere.com, you will need to use that domain when creating the instance. You don't need to use a valid SSL certificate though. The configurator tool has an option to generate one for you.

  3. Export the signing certificate to a file.

  4. Download the PsExec tool from SysInternals and run the command below to open the MMC console:

  5. Import the exported certificate to NETWORKSERVICE's My store.

  6. Log in to the Admin site and make a bunch of changes. Firstly, go to System Setup and change the location of the signing certificate to CurrentUser.

  7. Open the Logging page and change Log target to Text file. Uncheck the "Log user requests to Windows Event log" setting. You can also uncheck the "Log UserRequest to SQL database" setting if you want to reduce SQL database load due to using a low Azure SQL plan.

  8. Decrypt all encrypted sections in web.config files because Azure app service won't be able to decrypt them:

  9. Change the DefaultStoreLocation setting to CurrentUser.

  10. DefaultStoreLocation is CurrentUser. Hack database to change the validonly attribute of the signing certificate in System Setup to false.

  11. Restart the website in IIS and try a login to make sure it still works.

  12. Zip C:\Program Files\Safewhere\Identify\Tenants\azureapp\admin, C:\Program Files\Safewhere\Identify\Tenants\azureapp\runtime, Logs to a zip file.

  13. Zip C:\Program Files\Safewhere\OneAdmin\Tenants\azureapp\ to another zip file.

Deploy Admin and Runtime on Azure app service

Now you can deploy Admin and Runtime on Azure app service. In this section, you need to make all changes to the files deployed on Azure app service. In other words, do not make the following changes to the local files.

  1. Create a new Azure Service App:
    service-app

  2. On Azure Portal, navigate to https://identifyonazureapp.scm.azurewebsites.net/ZipDeployUI and upload the zip file containing the Admin and Runtime folders to /wwwroot. Reference: https://blogs.msdn.microsoft.com/benjaminperkins/2017/12/28/deploy-to-an-azure-app-service-using-kudu-and-a-zip-file/. If that doesn't work, you can try to open Debug console -> CMD, open the wwwroot folder and try to drop your zip file again.

  3. Result:

    service app

  4. Add path mappings:

    path mappings

  5. Turn on debug:

    azure-app-quick-debug

  6. The debug log screen is

    debug-log-screen

  7. Open WindsorAuditUserRequest.config and disable WindowsSecurityLogAuditUserRequest as well as enable NullSecurityAuditLogger:

  8. Change the LogFolderPath setting of the two web.config files:

  9. If you are using a self-signed signing certificate, you need to add a certificateValidation block to the Admin's web.config file:

  10. Add a firewall rule to your Azure SQL server to allow connections from your Azure app service:

  11. Upload the signing certificate:

    upload-signing-certificate

  12. Create Custom Domain:

    custom-domain

  13. Open the TLS/SSL settings tab, select Create App Service Managed Certificate provision SSL certificates:

    tls-ssl

  14. Create binding using the newly provisioned SSL cert:

    binding

  15. Open Configuration and add a new WEBSITE_LOAD_CERTIFICATES whose value is the thumbprint of the signing certificate:

    binding

Deploy Safewhere Admin

Create another Azure service app in the same Service plan because Safewhere Admin runs under Asp.Net Core.

  1. Upload file:

    upload-adminv2

  2. Add domain mapping, provision SSL certificate, add SSL binding.

  3. Edit the web.config file to change its stdoutLogFile setting to stdoutLogFile="\\?\%home%\LogFiles\stdout".

  4. Create a new OIDC connection for the site on Azure app service because the domain name is changed now.

Code changes

I made some quick code changes to get Identify work on Azure app service.

Event log

Azure App Service runs in a sandbox that does not have access to registry. Therefore, creating a custom event log source is not possible. Even though we can configure Identify to log to files or database, there is still an "error handler" that logs to event log. The "fix" is that I changed code to use Application for both source and application.

I also made a small change to always log the right stack trace.

Performance counter

Also due to the sandboxing restriction, I had to change code to disable all custom performance counters.

Troubleshooting

Addition to all the logs enabled above, you can open Advanced tools -> Debug console -> CMD and view eventlog.xml found in the LogFiles folder.

Try to restart your app services if things don't work right away. The apps sometimes didn't work for me, then they worked after an hour without any changes.

Please note that I also got a strange problem with ViewState on Azure app service. I didn't find out what the problem is but it won't be a big problem when we kill off the old Admin.

What's next

All the manual steps above show what we need to do to deploy Identify on Azure app service. We need to find out how to handle the following issues with minimum manual steps:

  1. Copy files to Azure app service.
  2. How to create a database. As-is, we use InstallUtil.exe to execute a bunch of installer classes defined in Safewhere's dlls to set up default data. One option is to copy the InstallUtil.exe file to the bin folders on Azure app service and run the installers there.
  3. Performance counters are not that important. We can replace them with Application Insights.
  4. Logging to Event Logs is still useful. We can try to log to Application and feed all logs to Azure Monitor.
  5. Revive the option to install Identify under CurrentUser mode.
  6. Option to disable logging to Windows Security Log totally.
  7. Move all secrets from web.config to Azure Key Vault. Even better, move all non-secretive settings from web.config to Azure app service's Configuration.
  8. Perform load test.
  9. Upgrade is another task to consider. Replicate is unnecessary.

Cost analysis

A minimum recommended VM plan is A3 or newer

INSTANCE CORES RAM STORAGE PRICES
A2 2 3.5 GiB 135 GiB ~$131.40/month
A3 4 7 GiB 285 GiB ~$262.80/month
A4 8 14 GiB 605 GiB ~$525.60/month
A5 2 14 GiB 135 GiB ~$248.20/month
A2 v2 2 4 GiB 20 GiB ~$94.90/month
A2m v2 2 16 GiB 20 GiB ~$137.24/month
A4 v2 4 8 GiB 40 GiB ~$200.02/month
A4m v2 4 32 GiB 40 GiB ~$287.62/month
A8 v2 8 16 GiB 80 GiB ~$419.75/month

Note that we need to deploy two VMs.

Meanwhile, cost of a standard service plan is:

INSTANCE CORES RAM STORAGE PRICES
S1 1 1.75 GB 50 GB ~$73/month
S2 2 3.50 GB 50 GB ~$146/month
S3 4 7 GB 50 GB ~$292/month

Premium service plan

INSTANCE CORES RAM STORAGE PRICES
P1v2 1 3.50 GB 250 GB ~$146/month
P2v2 2 7 GB 250 GB ~$292/month
P3v2 4 14 GB 250 GB ~$584/month

All the upcoming scenarios assume that we want to deploy in redundant setup which needs 2 VMs. If running 1 VM is acceptable, the VM option will always be cheaper than Azure app service.

I want to host one or 2 instances for testing

2 instances can need about 1.5 GiB of memory to run smoothly. A2 or A2v2 (~190-260 USD/month) may work but the instances might be a bit laggy due to lacking of RAM (note that the OS eats some RAM as well).

For Azure App Service, we can try S2. S1 might also work but again the instances might be laggy.

I want to host as many instances as possible

We need more RAM and more disk space. Since disk space is cheap, RAM is the decisive factor. A4m v2 VM has a clear advantage over P3v2.

I want to host a single production instance for my customer

Recommended plans are:

INSTANCE CORES RAM STORAGE PRICES
A2 2 3.5 GiB 135 GiB ~$131.40/month * 2
A3 4 7 GiB 285 GiB ~$262.80/month * 2
A4 8 14 GiB 605 GiB ~$525.60/month * 2
A2 v2 2 4 GiB 20 GiB ~$94.90/month * 2
A2m v2 2 16 GiB 20 GiB ~$137.24/month * 2
A4 v2 4 8 GiB 40 GiB ~$200.02/month * 2
S2 2 3.50 GB 50 GB ~$146/month
S3 4 7 GB 50 GB ~$292/month
P2v2 2 7 GB 250 GB ~$292/month

If you want to have 2 cores, S2 can be a good choice.

If you want to have 4 cores, S3 can be a better choice than A3 or A4v2.

However, please note that using 2 VMs means we can split traffics between them which can serve twice more traffics.

Note: we have done no performance testing against all those plans yet, except the A4 plan.