Umbraco

Migrating from Umbraco 8 on-premises to Umbraco 9 on Cloud

A candid tale of migrating

Candid Contributions
Written by Candid Contributions

An hour or so into the Hacktoberthon, we decided it might be a good time to make the move from Umbraco 8 to 9. With a handful of experienced developers from the Umbraco community and a sprinkling of HQ devs, we doubted there’d be a better time! The idea was raised to see what the experience of migrating our website CandidContributions.com - a Umbraco 8 ‘on-premise’ site - to Umbraco 9 running on Umbraco Cloud, would be like…. Were we crazy?

Spoiler alert: We weren’t. This post will take you through the hours between making that decision and having our website deployed live on Cloud. Yep - hours. Not days.

Getting started, taking stock.

Between the four of us at Candid Contributions, we knew plenty about .NET Framework but not all of us had worked with Umbraco 9 or .NET 5 yet. So, we started by taking a look at our current site and having a think about what the different frameworks might mean for us. Then, we decided what could be migrated without any changes, what couldn’t be migrated and what would need some changes before it ‘worked’. Finally, we made an inventory of integrations, Umbraco packages and anything else of note. That looked like this:

Current CanCon Website

  • Configuration
    • Umbraco version 8.5.3
    • Hosted on standalone Windows VM, SQL Server 2019
  • Solution Setup
    • Core project
    • Website project
  • Packages
    • Our.Umbraco.RobotsTxtEditor (v8.0.0)
    • uSync (v8.6.2)
  • Custom Code
    • Background tasks for retrieving content from Spreaker and GitHub Apis
    • A hijacked route
    • API controller for posting event registrations to MailChimp
    • API endpoints for a Discord bot to retrieve content from our back-office
  • No Custom Property Editors
  • Models builder: AppData mode
  • Frontend: Riot templates

RTFM

After that, docs. Was there anything in particular we needed to know about going from on-premise to cloud? We found this helpful piece of documentation explaining where to begin. Lo and behold, we had already taken the right approach with our inventory above! 

The article lists some prerequisites for a “smooth” migration to Cloud. It looked like it should be plain sailing ahead for us then because our site:

  • Had fewer content nodes than the plan allows (for the Starter Plan, that’s 500) ✅
  • Contained no member data ✅
  • Had no obsolete / old packages ✅ uSync isn’t needed in Umbraco Cloud, and we could survive without Robots.TxtEditor for now (as not yet ported to v9) 
  • Was not upgraded from a version below Umbraco 7 ✅

That said, after reading through this piece, we came up with some questions and thoughts of our own:

  • Do we need to get to the latest version of Umbraco 8 first?
  • What are background tasks in Umbraco9? Docs: Recurring service or Hangfire
  • Do we have any custom database tables? (The answer was no)
  • Is there a frontend build process that we’d need Cloud to run?

A vague plan

Before opening Visual Studio we thought it best to agree to a top-level plan of action:

  • Don’t worry about getting to Umbraco 8 latest; start a new Umbraco 9 site instead, point it at our existing Umbraco 8 database, and try the migration
  • Copy over the media and the frontend assets; the frontend build process can wait for another day
  • Import the views and custom code and get the site working locally with the minimum amount of changes
  • Push to Umbraco Cloud
  • Go live (oh the optimism!)

Time to get our hack on

With sleeves metaphorically rolled up, we began by creating a new Cloud project. A big thanks goes out to Warren Buckley, who spun up our very own environment, invited us as contributors, and set the ball rolling. We logged into the cloud portal and cloned the brand new project. So far so good.

Time to open Visual Studio! What we noticed, first of all, was how different a Umbraco 9 Cloud site looks when you first clone it. We had become accustomed to the quirks of working locally with Cloud, but here, we were looking at a site that closely resembled an on-premise set up. There’s no longer a need for separate repositories for your code and your site - the entire project lives in one repo. Umbraco Cloud comes with a .csproj file which can be opened directly with Visual Studio. There’s no work you need to do to turn the project into a solution to work with in VS or any other IDE. If you want a VS solution file you can add that via the dotnet CLI and adding a class library, as we did, or other projects, to the solution works as we’re used to from any other .NET project. Getting started on an Umbraco 9 Cloud site appeared to be simple for a seasoned Umbraco dev. If you want it in writing, there’s an excellent documentation article on how to work with Umbraco 9 and Visual Studio.

Migrating the database

Next, we knew we needed to bring our Umbraco 8 content into our brand new Umbraco 9 site. To do that, we would point our Umbraco 9 site at (a copy of!) our production Umbraco 8 database which would, in theory, migrate it over. Following the documentation, we found the appSettings.json file in our solution. We changed the connection string and hit run.

Umbraco 9 Screenshot Appsettings.Json config file

Then? We sat back and watched as everything happened exactly as it was supposed to. Nothing more to say about that, except that there were little victory dances when only a few minutes later, we had our content in a Umbraco 9 backoffice.

“Do we click ‘view report’ or is that for wimps?”

Battling the red lines

Never content with resting on our laurels, we made cups of tea and returned to our desks for more work. Time to copy over our media library. Copying our library into /wwwroot/media took care of that. Views and partials were next, and we were not surprised to see a whole lot of red squiggly lines when we copied these over and took a look. A change from one framework to another always requires a little (or a lot of) re-engineering.

In our case, adding a ‘using’ and an ‘inherits’ took care of many of the issues.

@using Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage

Then we could take a look and see what was still red. 

Some real changes came next. Umbraco 9, being based on .NET 5, asks that we approach some of our architectural choices differently: Dependency injection is a first-class citizen, async is encouraged, and we no longer have Razor helpers. We discovered this as we progressed, partials first, and then views; each time we encountered a change, we tried to find a new way to work rather than build the way we knew. We sometimes relied sometimes on intellisense (eg. is Html.PartialAsync a thing? Yes!) and used docs for things we couldn’t take educated guesses on. One thing we noticed was that while

@await RenderSectionAsync 

was a thing,

@await RenderBodyAsync 

was not. 

This stage of the build was more fun than it was arduous. Exploring a new framework in a group of developers is a fantastic way to learn how other people work and think. Sharing ideas, keyboard shortcuts and documentation - these are all things that happen at hackathons that are incredible for learning. Working in such a dynamic environment, even just for an afternoon, really helps us to be creative as technicians. 

Modelsbuilder

Modelsbuilder was our next port of call. We knew that the settings had been renamed in Umbraco 9 to better reflect the way that modelsbuilder works, and chose to set ModelsMode to ‘SourceDataAuto’. We’d love to tell you that we made this choice after much consideration but after a quick, “Which one do you use, Seb?”, we settled on that one. We then went back to appSettings.json and used autocomplete to add the modelsbuilder property and settings, also adding the path for the models to the class library project (which is where they had been before). 

Hot tip: if you change the location of your models, remember to a) manually generate the models in their new location via the backoffice and b) delete any previously generated models (Umbraco 9 stores these by default in /umbraco/models) and rebuild. Forgetting (b) kept us going around in circles for a while!

Models generated, we had even less red than before. Yay! But now we were running out of low-hanging fruit. To avoid this article getting too in-depth, we wrote up the migration tasks that we worked through as GitHub issues, so please hop over there if you want to find answers to questions such as:

  • Why doesn’t my Route Hijacking work?
  • What should we replace Razor Helpers with?
  • Is XPath still a thing?
  • What happened to IHtmlString?
  • Why oh why (oh why) is my API controller not working? 

Pushing to Cloud

Before we could push any content or media, we needed to get the ‘meta data’ of the site (Document Types, Data Types, etc.) deployed. Because all the Document and Data Types were already in the database and not created via the backoffice, this needed to be done manually using the ‘Extract Schema to Data Files’ option on the Deploy database in Settings:

Deploy dashboard in the settings section of the Umbraco backoffice.

We committed and pushed to Cloud. Unfortunately, we still weren’t able to push any content due to a conversion error. Because we had an older Umbraco 8 site, we were using the old Media Picker-Data Type. Since the new Media Picker and the legacy Media Picker data types are both called “Media Picker”, the legacy one had been renamed “Media Picker (1)” when deployed, which was a mismatch with our local environment. Our resolution was to rename the new one on Cloud to something else (confession: we called it “Media Picker Fancy Pants”) so that we could then rename the legacy one back to “Media Picker”.

Success: a working Cloud site with all our content and media 🎉

Candid Contributions Website Screenshot - Green and grey background and a picture of the four members, Carole, Laura, Emma and Lotte, at the bottom.

The icing on the cake

When everything necessary was working and pushed to the cloud, we figured we should just switch to the new site. No clients to ask for approval, after all! A few Cloudflare settings changed later and boom… our Umbraco 9 site https://candidcontributions.com was now live from Umbraco Cloud! Achievement unlocked - and the site is now so much faster. H5YR to everyone who worked on Umbraco 9!

Working in public

We are very grateful to everyone who accompanied us on this adventure at the Hackathon, especially those who stuck around to help us get over the finish line. It can be quite stressful working in public like this, but actually we can’t think of a more fun way to learn all the (many) things we learned about Umbraco Cloud and Umbraco 9. 

“… our Umbraco 9 site ... was now live from Umbraco Cloud! Achievement unlocked - and the site is now so much faster. H5YR to everyone who worked on Umbraco 9!”

We hope the contributions don’t stop there, though. Unfortunately, you can’t mirror a cloud repo on GitHub without revealing some secrets…so we’ve created a separate GitHub repository with most of the Cloud codebase so if you would like to make any comments about how we’ve implemented something, or perhaps even help with any remaining issues (IHttpActionResult replacement anyone?), please do! We’ll endeavour to keep the two repos in sync, so pull requests are welcome, despite it being too late for Hacktoberfest 2021 😉

 

Who, or what, is Candid Contributions?

Candid Contributions is the aptly named podcast where four experienced developers: Carole Logan, Emma Burstow, Laura Weatherhead and Lotte Pitcher talk all things open source - from code contributions to conference attendance - all aspects of life as an active member of an open-source community. 

The Candid Contributions team: Carole Logan, Emma Burstow, Laura Weatherhead and Lotte Pitcher

The Candid Contributions team has also organised community events such as CanCon Umbrackathon and Code Patch. You can read more about them on candidcontributions.com, where you can also find links to the podcast and more.