Umbraco 13 app deployment to azure web app issue

I have built an Umbraco 13 app locally using a tutorial and it connects to an sql database.
Now I want to deploy it so I’ve created an app service on azure (web app + database) and that’s fine too. I’ve got the database all good and populated, I can connect to it locally and see the content and I can run my Umbraco app locally (Visual studio IIS button) while connecting to the deployed database and it displays everything correctly.
The issue I have is with the actual web app part and the umbraco repo pipeline (I think?).

I’ve got my repo hosted on azure devops and my pipeline looks like this:

# ASP.NET Core
trigger:
- master

pool:
  vmImage: windows-latest

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
- task: NuGetToolInstaller@1

- task: NuGetCommand@2
  inputs:
    restoreSolution: '$(solution)'
- task: VSBuild@1
  inputs:
    solution: '$(solution)'
    msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: '$(build.artifactStagingDirectory)'
    artifactName: 'drop'
    publishLocation: 'Container'
- task: AzureRmWebAppDeployment@5
  inputs:
    ConnectionType: 'AzureRM'
    azureSubscription: 'Azure subscription(xxxxxxxx)'
    appType: 'webAppLinux'
    WebAppName: 'myapp'
    packageForLinux: '$(build.artifactStagingDirectory)/**/*.zip'
    RuntimeStack: 'DOTNETCORE|8.0'
    DeploymentTypeLinux: 'oneDeploy'
# Build and test ASP.NET Core projects targeting .NET Core.
# Add steps that run tests, create a NuGet package, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core

I’ve got this appsettings.json in my repo:

{
  "$schema": "appsettings-schema.json",
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information",
        "System": "Warning"
      }
    }
  },
  "Umbraco": {
    "CMS": {
      "Global": {
        "Id": "some-id-xxxxx",
        "SanitizeTinyMce": true,
        "MainDomLock": "FileSystemMainDomLock"
      },
      "Hosting": {
        "LocalTempStorageLocation": "EnvironmentTemp"
      },
      "Examine": {
        "LuceneDirectoryFactory": "SyncedTempFileSystemDirectoryFactory"
      },
      "Content": {
        "AllowEditInvariantFromNonDefault": true,
        "ContentVersionCleanupPolicy": {
          "EnableCleanup": true
        }
      },
      "Unattended": {
        "UpgradeUnattended": true
      },
      "Security": {
        "AllowConcurrentLogins": false
      },
      "RichTextEditor": {
        "CustomConfig": {
          "style_formats": "[{\"title\":\"Paragraphs\",\"items\":[{\"title\":\"Normal\",\"block\":\"p\"},{\"title\":\"Lead\",\"block\":\"p\",\"attributes\":{\"class\":\"lead\"}}]},{\"title\":\"Headings\",\"items\":[{\"title\":\"h1\",\"block\":\"h1\"},{\"title\":\"h2\",\"block\":\"h2\"},{\"title\":\"h3\",\"block\":\"h3\"},{\"title\":\"h4\",\"block\":\"h4\"},{\"title\":\"h5\",\"block\":\"h5\"},{\"title\":\"h6\",\"block\":\"h6\"}]},{\"title\":\"Text Colours\",\"items\":[{\"title\":\"Primary\",\"inline\":\"span\",\"styles\":{\"color\":\"#1abc9c\"},\"attributes\":{\"class\":\"text-primary\"}},{\"title\":\"Secondary\",\"inline\":\"span\",\"styles\":{\"color\":\"#03203d\"},\"attributes\":{\"class\":\"text-secondary\"}},{\"title\":\"Success\",\"inline\":\"span\",\"styles\":{\"color\":\"#198754\"},\"attributes\":{\"class\":\"text-success\"}},{\"title\":\"Danger\",\"inline\":\"span\",\"styles\":{\"color\":\"#dc3545\"},\"attributes\":{\"class\":\"text-danger\"}},{\"title\":\"Warning\",\"inline\":\"span\",\"styles\":{\"color\":\"#ffc107\"},\"attributes\":{\"class\":\"text-warning\"}},{\"title\":\"Info\",\"inline\":\"span\",\"styles\":{\"color\":\"#0dcaf0\"},\"attributes\":{\"class\":\"text-info\"}},{\"title\":\"Light\",\"inline\":\"span\",\"styles\":{\"color\":\"#f8f9fa\"},\"attributes\":{\"class\":\"text-light\"}},{\"title\":\"Dark\",\"inline\":\"span\",\"styles\":{\"color\":\"#212529\"},\"attributes\":{\"class\":\"text-dark\"}},{\"title\":\"White\",\"inline\":\"span\",\"styles\":{\"color\":\"#ffffff\"},\"attributes\":{\"class\":\"text-white\"}}]}]"
        }
      }
    }
  },
  "Slimsy": {
    "WidthStep": 80,
    "UseCropAsSrc": false,
    "DefaultQuality": 70,
    "Format": "",
    "BackgroundColor": "",
    "AppendSourceDimensions": true,
    "EncodeCommas": true,
    "AutoOrient": true
  },
  "ConnectionStrings": {
    "umbracoDbDSN": "Server=tcp:myapp-server.database.windows.net,1433;Initial Catalog=myapp-database;Persist Security Info=False;User ID=myapp-server-admin;Password=MyPasswordXXX;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;",
    "umbracoDbDSN_ProviderName": "Microsoft.Data.SqlClient"
  }
}

I’ve got a few more things in an appsettings.Development.json:

{
  "$schema": "appsettings-schema.json",
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information"
    },
    "WriteTo": [
      {
        "Name": "Async",
        "Args": {
          "configure": [
            {
              "Name": "Console"
            }
          ]
        }
      }
    ]
  },
  "ConnectionStrings": { --- same as in appsettings.json ---},
  "Umbraco": {
    "CMS": {
      "Unattended": {
        "InstallUnattended": true,
        "UnattendedUserName": "Administrator",
        "UnattendedUserEmail": "[email protected]",
        "UnattendedUserPassword": "example-password"
      },
      "Content": {
        "MacroErrors": "Throw"
      },
      "Hosting": {
        "Debug": true
      },
      "RuntimeMinification": {
        "UseInMemoryCache": true,
        "CacheBuster": "Timestamp"
      },
      "ModelsBuilder": {
        "ModelsMode": "SourceCodeManual",
        "ModelsDirectory": "~/Models/Generated"
      }
    }
  }
}

And I don’t have any other appsettings.
I think I’m aligned with the Umbraco recommendations mentioned here and I’ve got the ASPNETCORE_ENVIRONMENT variable set on my azure portal in my app service details (web app > settings > environment variables) with value ‘Production’.
At this point, my pipeline runs successfully and I can see on my azure portal (in Deployment center) that the last deployment is noted as successful, but when I click the url, I land on the usual azure empty app waiting for content, and I don’t know what to do to fix it. I just can’t find the right help out there, the posts are either too generic or about different more complex issues.
Can anyone help me please? I’m new at C# (and VS) and I’ve never deployed a C# & CMS app and I don’t know where to go from here. Thanks a lot in advance for your help!

Ah pipelines, my favorite horror. I don’t have a definitive answer for you, but I think it’s best if you use ‘newer’ tasks. This also enables you to use Linux agents that are in my experience faster. Some pointers:

    # Publish the project to the staging directory
    # This implicitly does a restore and a build
  - task: DotNetCoreCLI@2
    displayName: '.NET: publish solution'
    inputs:
      command: 'publish'
      arguments: '--configuration ${{ variables.buildConfiguration}} --output $(Build.ArtifactStagingDirectory)/${{ variables.solutionName }}'
      zipAfterPublish: false
      modifyOutputPath: false

    # Create an archive that will function as the artifact
  - task: ArchiveFiles@2
    displayName: 'PUBLISH: Create archive for deploy'
    inputs:
      rootFolderOrFile: '$(build.artifactstagingdirectory)/${{ variables.solutionName }}'
      includeRootFolder: false
      archiveType: 'zip'
      archiveFile: '$(Build.ArtifactStagingDirectory)/artifactzip/${{ variables.solutionName }}.zip'
      replaceExistingArchive: true
      continueOnError: true

    # Publish the artifact to the artifact staging directory
  - task: PublishBuildArtifacts@1
    displayName: 'PUBLISH: Upload artifact'
    inputs:
      pathtoPublish: '$(Build.ArtifactStagingDirectory)/artifactzip'
      artifactName: '${{ variables.solutionName }} - $(Build.BuildNumber)'
      publishLocation: 'Container'

After this you have an artifact. Notice that I build the artifact zip here manually, because in my pipeline there are a few manual steps that copy over files that will be included in the zip this way.

Then we need to deploy the artifact:

    # Deploy the artifact to Azure App Service
  - task: AzureRmWebAppDeployment@4
    displayName: 'DEPLOY: Azure app service deployment'
    inputs:
      ConnectionType: 'AzureRM'
      azureSubscription: '${{ variables.resourceManagerConnection }}'
      appType: 'webAppLinux'
      WebAppName: '${{ variables.appServiceName }}'
      packageForLinux: '$(System.ArtifactsDirectory)/**/*.zip'
      AppSettings: '-ASPNETCORE_ENVIRONMENT ${{ parameters.environment }} -TZ "Europe/Amsterdam" -Version $(Build.BuildNumber)'
      enableCustomDeployment: true
      DeploymentType: 'runFromPackage'
      TakeAppOfflineFlag: true

I just copied over some tasks of the pipelines I use, so I didn’t check if all variables are present and matching, but it’ll have a good starting point for you.

Hi Luuk,
Thanks for the suggestion, I’ve tried this pipeline with adjustments where needed with my own values, it also looks like it goes through fine, and I’m now thinking maybe the pipeline wasn’t the problem…

I’ve actually made some progress, because I’ve discovered that the backoffice is actually ok, I can log in and see my content tree, it’s not empty! (I didn’t think of looking at that yesterday)
So it’s mainly the visitor-side which is the problem, and I don’t get why one side would work and not the other. I’m overall confused I guess.
What could be going wrong?

My web app is on linux.

If that helps at all, when I visit the url, I get this error in the network tab:

But I can go in my backoffice:

If you check the logs (Under Settings in the backoffice), are you getting any logged errors there that could give you a hint of what’s wrong?

Ah yes indeed! There are errors!
And most of them seem to be related to the packages listed in my appsettings.json (ModelsBuilder, RichTextEditor which has a customConfig in there). How come it can’t apply what it needs? Is it not finding the configs?
One was db connection related (I resolved that one I think!)

I would try to change the ModelsMode setting to “SourceCodeManual”
Go to Settings in the backoffice (your local environment), hit Generate Models.
That should generate a bunch of .cs files that you should be able to compile.
Include those in a new deploy and see if that solves atleast some of the issues.

(You can also do the “None” setting for your azure environments, so you can’t accidently generate new files on the other environments).

Thanks Mats and Luuk, your suggestions did really help me look around and find that:

  • I had some stuff missing in my appsettings.json so some packages weren’t considered in the publish, only on dev
  • I had not included wwwroot files in the publish either
    I’ve gone through a few steps online to sort that and at some point saw my website but with no styles applied, so tried a couple more things and now I’ve committed my wwwroot files onto my repo but I’m at the same point as some other people out there which is the Umbraco Installer page showing up on app rebuild, which is really frustrating because I know my db connection was ok and it’s not empty, and if anything builds under that, I can’t see it…
    The debugging continues, and hopefully it won’t take over my whole weekend :crossed_fingers:
    I’m a nuub at Umbraco13 but I feel there’s definitely stuff around deployment that could be detailed in the documentation a bit better? :sob: