Tag Archives: deployment

Production Tips for ASP .NET Core 3.1 Web Apps

By Shahed C on April 20, 2020

This is the sixteenth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

P is for Production Tips

After getting through more than halfway in this A-Z series, this blog post takes a step back from application code to focus on productions tips. Once you’re ready to deploy (and maintain) your web app in production, there are many tips and tricks you should be aware of. In fact, feel free to discuss with your team members and the dev community to learn about other ways developers are deploying in production.

Server Environments from Dev to Prod via Staging
Server Environments from Dev to Prod via Staging

While this article focuses on deployments to Azure App Service, you can use some of the lessons learned for your own environments. That being said, I would highly recommend taking a look at Azure for all your staging and production deployment needs.

Deployment Slots

Azure makes it very easy to deploy your ASP .NET Core web application with the use of Deployment Slots. Instead of publish a web app directly to production or worrying about downtime, you can publish to a Staging Slot and the perform a “swap” operation to essentially promote your Staging environment into Production.

NOTE: To enable multiple deployment slots in Azure, you must be using an App Service in a Standard, Premium, or Isolated tier.

If you need help creating a Web App in App Service, you may refer to my blog post on the topic:

To make use of deployment slots for your Web App:

  1. Log in to the Azure Portal.
  2. Create a new Web App if you haven’t done so already.
  3. Locate the App Service blade for your Web App
  4. Enter the Deployment Slots item under Deployment
  5. Click + Add Slot to add a new slot
  6. Enter a Name, chose a source to clone settings (or not)
  7. Click the Add button to create a new slot
Deployment Slots in Azure App Service
Deployment Slots in Azure App Service

Once you set up multiple slots for staging and production, you may use the Swap feature to swap your deployed application when the staged deployment is ready to be deployed into production. Note that all slots are immediately live at the specified endpoints, e.g. hostname.azurewebsite.net.

You may also adjust website traffic by setting the Traffic % manually. From the above screenshot, you can see that the Traffic % is initially set to 0 for the newly-created slot. This forces all customer traffic to go to the Production slot by default.

When deploying your application through various means (Visual Studio Publish, Azure CLI, CI/CD from your Source Control System, etc), you may choose the exact slot when there is more than one. You may also set up “Auto-Swap” to swap a slot (e.g. staging) automatically into production, upon pushing code into that slot.

To learn more about all of the above, check out the official docs at:

Environment Configuration

To maintain unique configuration settings for each environment (e.g. staging database vs production database connection strings), you should have unique configuration settings for each environment. This is easily accomplished using the Configuration section in the Settings category of each slot’s unique blade.

Configuration screen in Azure App Service
Configuration screen in Azure App Service

NOTE: If you need help with User Secrets for your development environment or Key Vault secrets for your server environment, consider the following posts from my 2018 series and earlier in this 2020 series:

EF Core Migrations

You may be wondering how you can deploy structural changes from your database into production. Perhaps, you write manual SQL scripts to run in production, maybe you use a tool to generate such SQL scripts or a combination of both. Many developers aren’t aware but you can actually make use of Entity Framework Core (EF Core) Migrations to update your database structure.

To get a quick refresher on EF Core Migrations and Relationships, check out the following post:

You wouldn’t typically run your “Update Database” command in production. Instead, you could generate a SQL Script from your EF Core Migrations. This will allow you to inspect the SQL Scripts (revise them if necessary), hand them over to a DBA if appropriate and finally run the SQL Scripts in production when required.

The following PowerShell command can be run in Visual Studio’s Package Manager Console panel:

Script-Migration

The following CLI Command can be run on a Command Prompt, PowerShell prompt or VS Code Terminal window:

dotnet ef migrations script

You may set specific migrations to start from and/or end on:

Script-Migration -To <starting-migration>
Script-Migration -From <ending-migration>

You may also dump out the SQL scripts into a file for further inspection:

Script-Migration -Output "myMigrations.sql"

Scalability

If you’re deploying your web apps to Azure App Service, it’s a no-brainer to take advantage of scalability features. You could ask Azure to scale your app in various ways:

  • Scale Up: Upgrade to a more powerful (and higher priced) tier to add more CPU, memory and disk space. As you’ve seen with the appearance of staging slots, upgrading to a higher tier also provides additional features. Other features include custom domains (as opposed to just subdomains under azurewebsites.net) and custom certificates.
  • Scale Out: Upgrade the number of VM instances that power your web app. Depending on your pricing tier, you can “scale out” your web app to dozens of instances.
  • Autoscaling: When scaling out, you can choose when to scale out automatically:
    • Based on a Metric: CPU %, Memory %, Disk Queue Length, Http Queue Length, Data In and Data Out.
    • Up to a specific Instance Count: set a numeric value for the number of instances, set minmium and maximum.

An example of autoscaling on a metric could be: “When the CPU% is >50%, increase instance count by 1“. When you had new scaling conditions, you may also set a schedule to start/end on specific dates and also repeated on specific days of the week.

Adding a Scale Rule to Scale Out in Azure App Service
Adding a Scale Rule to Scale Out in Azure App Service

NOTE: In order to make use of Auto-Scaling, you’ll have to upgrade to the appropriate tier to do so. You can still use Manual Scaling at a lower tier. Scalability features are not available on the F1 Free Tier.

CI/CD

There are countless possibilities to make use of CI/CD (Continuous Integration and Continuous Deployment) to make sure that your code has been merged properly, with unit tests passing and deployed into the appropriate server environments. Some of your options may include one of the following: Azure Pipelines, GitHub Actions, or some other 3rd party solution.

  • Azure Pipelines: an offering of Azure DevOps services, you can quickly set up CI/CD for your web app, both public and private.
  • GitHub Actions: available via GitHub, the relatively-new Actions feature allows you to automate your workflow

The Deployment Center feature in Azure’s App Service makes it very easy to select Azure Pipelines (under Azure Repos) for your web app. This is all part of Azure DevOps Services, formerly known as VSTS (Visual Studio Team Services)

Deployment Center in Azure App Service
Deployment Center in Azure App Service

To get started with the above options, check out the official docs at:

TeamCity and Octopus Deploy are also popular products in various developer communities. Whatever you end up using, make sure you and your team select the option that works best for you, to ensure that you have your CI/CD pipeline set up as early as possible.

Troubleshooting

Once your application has been deployed, you may need to troubleshoot issues that occur in Production. You can use a combination of techniques, including (but not limited to) Logging, Error Handling and Application Insights.

  • Logging: From ASP .NET Core’s built-in logging provider to customizable structured logging solutions (such as Serilog), logging helps you track down bugs in any environment.
  • Error Handling: Anticipating errors before they occur, and then logging errors in production help you
  • Application Insights: Enabled by default in Azure’s App Service, Application Insights literally give you insight into your web application running in a cloud environment.

For more information on Logging and Error Handling, check out the earlier posts in this series:

For more information on Application Insights, check out the official documentation at:

References

Deploying ASP .NET Core 3.1 to Azure App Service

By Shahed C on January 27, 2020

This is the fourth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

D is for Deploying to Azure App Service

In this article, we’ll explore several options for deploying an ASP .NET Core web app to Azure App Service in the cloud. From the infamous Right-Click-Publish to fully automated CI/CD, you’ll learn about the latest Deployment Center option in the Azure Portal for App Service for web apps.

NOTE: If you’re looking for information on deploying to Docker or Kubernetes, please check out the following docs instead:

Right-Click Publish (aka Friends Don’t Let Friends Right-Click Publish)

If you’ve made it this far, you may be thinking one of the following:
a. “Hey, this is how I deploy my web apps right now!”
or
b. “Hey wait a minute, I’ve heard that you should never do this!”

Well, there is a time and place for right-click publish. There have been many debates on this, so I won’t go into the details, but here are some resources for you to see what others are saying:

So, what’s a web developer to do? To quote from the aforementioned MSDN article, “Continuing with the theme of prototyping and experimenting, right click publish is the perfect way for existing Visual Studio customers to evaluate Azure App Service (PAAS). By following the right click publish flow you get the opportunity to provision new instances in Azure and publish your application to them without leaving Visual Studio:”

In other words, you can use this approach for a quick test or demo, as shown in the screenshots below for Visual Studio.

  1. Right-click your ASP .NET Core web app project in Solution Explorer and select Publish.
  2. Click the Start button on the screen that appears and follow the onscreen instructions.
  3. Ensure that you’re logged in to the correct Azure subscription account you want to publish to.
Right-click, Publish from Solution Explorer
Right-click, Publish from Solution Explorer
Pick a Publish Target
Pick a Publish Target

Web Apps in the Azure Portal

In the screenshot above, you may notice an option to “Import Profile” using the button on the lower left. This allows you to import a Web App profile file that was generated by exporting it from an existing Azure Web App. To grab this profile file, simply navigate to your existing Web App in the Azure Portal and click on “Get publish profile” in the top toolbar of your Web App, shown below:

Get Publish Profile
Get Publish Profile

If you want to create a new Web App in Azure starting with the Azure Portal, follow the instructions below:

  1. Log in to the Azure at https://portal.azure.com
  2. On the top left, click + Create a resource
  3. Select “Web App” or search for it if you don’t see it.
  4. Enter/select the necessary values:
    • Subscription (select a subscription)
    • Resource Group (create or use existing to group resources logically)
    • Web App name (enter a unique name)
    • Publish (Code or Docker Image)
    • Runtime stack (.NET Core)
    • App Service Plan (create or use existing to set location and pricing tier)
    • OS (Windows or Linux)
    • Region (e.g. East US)
  5. Click Next through the screens and then click the Create button to complete the creation of your new Web App.
Create New Web App
Create New Web App

Now you can deploy to this Web App using any method you choose.

Runtime Options

If you like to stay ahead of ASP .NET Core releases, you may be using a pre-release version of the runtime. As of this writing, the latest stable version of ASP .NET Core is version 3.1, which is already available on Azure. If you’re looking for future preview releases, Azure App Service also has an option to install an Extension for preview runtimes.

To find the proper runtime:

  1. Navigate to your Web App in the Azure Portal.
  2. Click on Extensions under Development Tools.
  3. Click + Add to add a new extension.
  4. Choose an extension to configure required settings.
  5. Accept the legal terms and complete the installation.
Add Extension
Add Extension

Your options may include both 32-bit (x86) and 64-bit (x64) versions of the ASP .NET Core runtime and any preview versions of future releases. When planning ahead for multiple environments, you also have the option to deploy to Deployments Slots. This feature is available in StandardPremium or Isolated App Service Plan tiers and will covered in a future blog post in this series.

If you’re interested in Deployment Slots right now, check out the official docs at:

Deployment Center

In the list of features for your Web App, you will find an option to open up the new Deployment Center. Note that this has replaced the old Deployment Options. Let’s go over each of these options:

  1. Azure Repos
  2. Github
  3. Bitbucket
  4. Local Git
  5. OneDrive
  6. Dropbox
  7. External
  8. FTP
Deployment Options in Deployment Center
Deployment Options in Deployment Center
More Deployment Options
More Deployment Options

If you choose Azure Repos, you can set up your web app’s CI (Continuous Integration) system with an Azure Repo, which is part of Microsoft’s Azure DevOps services (formerly known as VSTS, aka Visual Studio Team Services). You will have options for using App Service as a Kudu build server or Azure Pipelines as your CI build system.

 Azure Repos choices: Kudu or Pipelines?
Azure Repos choices: Kudu or Pipelines?

If you choose Github or BitBucket or even a local Git account, you’ll have the ability to authorize that account to publish a specific repo, every time a developer pushes their code.

 Authorize Github/Bitbucket
Authorize Github/Bitbucket

If you choose OneDrive or DropBox, you’ll have ability to authorize your App Service to pick up files deployed to a shared folder in either location.

 Authorize OneDrive/DropBox
Authorize OneDrive/DropBox

You may also select an External repo or FTP source. To learn more about Azure Repos and Azure Pipelines, check out the official docs:

GitHub Repos (includes FREE option!)

If you’ve been using GitHub for public open-source projects or private projects on paid accounts, now is a great to time to create private repositories for free! In 2019, GitHub started offering free unlimited private repos, limited to 3 collaborators. This new free option comes with issue/bug tracking and project management as well.

For more information on GitHub pricing, check out their official pricing page:

 GitHub pricing: Free vs Pro, Team and Enterprise
GitHub pricing: Free vs Pro, Team and Enterprise

Now you can easily set up your starter projects in a private GitHub repository and take advantage of the aforementioned CI/CD setup without having to choose between paying a GitHub fee or making all your repos public.

CLI Commands

If you wish to publish to Azure App service using CLI (Command Line Interface) Commands, you may use the following commands, where you can choose a name for your Web App, Resource Group, App Sevice Plan, etc. Single-letter flags are usually preceded by a single hyphen, while flags spelled out with completed words are usually preceded by two hyphens.

First, install the Azure CLI in case you don’t have it already:

Authenticate yourself:

> az login

Create a new resource group:

> az group create -l <LOCATION> -n <RSG>
> az group create --location <LOCATION> --name <RSG>

Create a new App Service Plan, where <SKUCODE> sku may be F1 or FREE, etc

> az appservice plan create -g <RSG> -n <ASP> --sku <SKUCODE> 
> az appservice plan create --resource-group <RSG> --name <ASP> --sku <SKUCODE>

From the documentation, the SKU Codes include: F1(Free), D1(Shared), B1(Basic Small), B2(Basic Medium), B3(Basic Large), S1(Standard Small), P1V2(Premium V2 Small), PC2 (Premium Container Small), PC3 (Premium Container Medium), PC4 (Premium Container Large), I1 (Isolated Small), I2 (Isolated Medium), I3 (Isolated Large).

Create a new Web App within a Resource Group, attached to an App Service Plan:

> az webapp create -g <RSG> -p <ASP> -n <APP> 
> az webapp create --resource-group <RSG> --plan <ASP> --name <APP>

The above command should create a web app available at the following URL:

  • http://<APP>.azurewebsites.net

To push your commits to a Git Repo and configure for App Service Deployment, use the following CLI commands:

Create a new Git repo or reinitialize an existing one:

> git init

Add existing files to the index:

> git add .

Commit your changes with a commit message:

> git commit -m "<COMMIT MESSAGE>“

Set your FTP credentials and Git deployment credentials for your Web App:

> az webapp deployment user set --user-name <USER>

Configure an endpoint and add it as a git remote.

> az webapp deployment source config-local-git -g <RSG> -n <APP> --out tsv  
> az webapp deployment source config-local-git --resource-group <RSG> --name <APP> --out tsv
> git remote add azure <GIT URL>

The value for GIT URL is the value of the Git remote, e.g.

  • https://<USER>@<APP>.scm.azurewebsites.net/<APP>.git

Finally, push to the Azure remote to deploy your Web App:

> git push azure master

For more information on CLI Commands for Git and Azure App Service, check out the official docs:

Azure DevOps and YAML

Wait, what about Azure DevOps and YAML and Pipelines?

Since this is an A-Z series, you will have to wait for “Y is for YAML” to get more detailed information on constructing your build pipeline using YAML in Azure DevOps. If you can’t wait that long, feel free to check out the following .yml sample I uploaded for use with an ASP .NET Core 3.1:

If you’re reading this after June 2020, simply jump right to the “Y is for YAML” post in the 2020 A-Z series.

BONUS: for Azure SQL Database Deployment, watch the following video on MSDN Channel 9:

References

Production Tips for ASP .NET Core Web Apps

By Shahed C on April 22, 2019

This is the sixteenth of a series of posts on ASP .NET Core in 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

P is for Production Tips

After getting through more than halfway in this A-Z series, this blog post takes a step back from application code to focus on productions tips. Once you’re ready to deploy (and maintain) your web app in production, there are many tips and tricks you should be aware of. In fact, feel free to discuss with your team members and the dev community to learn about other ways developers are deploying in production.

From development to server environments

From development to server environments

While this article focuses on deployments to Azure App Service, you can use some of the lessons learned for your own environments. That being said, I would highly recommend taking a look at Azure for all your staging and production deployment needs.

Deployment Slots

Azure makes it very easy to deploy your ASP .NET Core web application with the use of Deployment Slots. Instead of publish a web app directly to production or worrying about downtime, you can publish to a Staging Slot and the perform a “swap” operation to essentially promote your Staging environment into Production.

NOTE: To enable multiple deployment slots in Azure, you must be using an App Service in a Standard, Premium, or Isolated tier.

If you need help creating a Web App in App Service, you may refer to my blog post on the topic:

To make use of deployment slots for your Web App:

  1. Log in to the Azure Portal.
  2. Create a new Web App if you haven’t done so already.
  3. Locate the App Service blade for your Web App
  4. Enter the Deployment Slots item under Deployment
  5. Click + Add Slot to add a new slot
  6. Enter a Name, chose a source to clone settings (or not)
  7. Click the Add button to create a new slot

AspNetCore-Prod-Slots

You may now use the Swap feature to swap your deployed application between staging and production when the staged deployment is ready to be deployed into production. Note that all slots are immediately live at the specified endpoints, e.g. hostname.azurewebsite.net.

You may also adjust website traffic by setting the Traffic % manually. From the above screenshot, you can see that the Traffic % is initially set to 0 for the newly-created slot. This forces all customer traffic to go to the Production slot by default.

When deploying your application through various means (Visual Studio Publish, Azure CLI, CI/CD from your Source Control System, etc), you may choose the exact slot when there is more than one. You may also set up “Auto-Swap” to swap a slot (e.g. staging) automatically into production, upon pushing code into that slot.

To learn more about all of the above, check out the official docs at:

Environment Configuration

To maintain unique configuration settings for each environment (e.g. staging database vs production database connection strings), you should have unique configuration settings for each environment. This is easily accomplished using the Configuration section in the Settings category of each slot’s unique blade.

AspNetCore-Prod-Slots-Staging-Config

NOTE: If you need help with User Secrets for your development environment or Key Vault secrets for your server environment, consider the following posts from my 2018 series and earlier in this 2019 series:

EF Core Migrations

You may be wondering how you can deploy structural changes from your database into production. Perhaps, you write manual SQL scripts to run in production, maybe you use a tool to generate such SQL scripts or a combination of both. Many developers aren’t aware but you can actually make use of Entity Framework Core (EF Core) Migrations to update your database structure.

To get a quick refresher on EF Core Migrations and Relationships, check out the following post:

You wouldn’t typically run your “Update Database” command in production. Instead, you could generate a SQL Script from your EF Core Migrations. This will allow you to inspect the SQL Scripts (revise them if necessary), hand them over to a DBA if appropriate and finally run the SQL Scripts in production when required.

The following PowerShell command can be run in Visual Studio’s Package Manager Console panel:

Script-Migration

The following CLI Command can be run on a Command Prompt, PowerShell prompt or VS Code Terminal window:

dotnet ef migrations script

You may set specific migrations to start from and/or end on:

Script-Migration -To <starting-migration>
Script-Migration -From <ending-migration>

You may also dump out the SQL scripts into a file for further inspection:

Script-Migration -Output "myMigrations.sql"

Scalability

If you’re deploying your web apps to Azure App Service, it’s a no-brainer to take advantage of scalability features. You could ask Azure to scale your app in various ways:

  • Scale Up: Upgrade to a more powerful (and higher priced) tier to add more CPU, memory and disk space. As you’ve seen with the appearance of staging slots, upgrading to a higher tier also provides additional features. Other features include custom domains (as opposed to just subdomains under azurewebsites.net) and custom certificates.
  • Scale Out: Upgrade the number of VM instances that power your web app. Depending on your pricing tier, you can “scale out” your web app to dozens of instances.
  • Autoscaling: When scaling out, you can choose when to scale out automatically:
    • Based on a Metric: CPU %, Memory %, Disk Queue Length, Http Queue Length, Data In and Data Out.
    • Up to a specific Instance Count: set a numeric value for the number of instances, set minmium and maximum.

An example of autoscaling on a metric could be: “When the CPU% is >50%, increase instance count by 1“. When you had new scaling conditions, you may also set a schedule to start/end on specific dates and also repeated on specific days of the week.

AspNetCore-Prod-Scale-Out

NOTE: In order to make use of Auto-Scaling, you’ll have to upgrade to the appropriate tier to do so. You can still use Manual Scaling at a lower tier. Scalability features are not available on the F1 Free Tier.

CI/CD

There are countless possibilities to make use of CI/CD (Continuous Integration and Continuous Deployment) to make sure that your code has been merged properly, with unit tests passing and deployed into the appropriate server environments. Some of your options may include one of the following: Azure Pipelines, GitHub Actions, or some other 3rd party solution.

  • Azure Pipelines: an offering of Azure DevOps services, you can quickly set up CI/CD for your web app, both public and private.
  • GitHub Actions: available via GitHub, the new Actions feature allows you to automate your workflow

The Deployment Center feature in Azure’s App Service makes it very easy to select Azure Pipelines (under Azure Repos) for your web app. This is all part of Azure DevOps Services, formerly known as VSTS (Visual Studio Team Services)

AspNetCore-Prod-Deploy

To get started with the above options, check out the official docs at:

TeamCity and Octopus Deploy are also popular products in various developer communities. Whatever you end up using, make sure you and your team select the option that works best for you, to ensure that you have your CI/CD pipeline set up as early as possible.

Troubleshooting

Once your application has been deployed, you may need to troubleshoot issues that occur in Production. You can use a combination of techniques, including (but not limited to) Logging, Error Handling and Application Insights.

  • Logging: From ASP .NET Core’s built-in logging provider to customizable structured logging solutions (such as Serilog), logging helps you track down bugs in any environment.
  • Error Handling: Anticipating errors before they occur, and then logging errors in production help you
  • Application Insights: Enabled by default in Azure’s App Service, Application Insights literally give you insight into your web application running in a cloud environment.

For more information on Logging and Error Handling, check out the earlier posts in this series:

For more information on Application Insights, stay tuned for an upcoming post in my next series that will focus on various Azure-related topics for ASP .NET Core developers.

References

 

Deploying ASP .NET Core to Azure App Service

By Shahed C on January 28, 2019

This is the fourth of a new series of posts on ASP .NET Core for 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

Before you begin, make sure you sign in to Azure, or create a new trial account first to follow along.

D is for Deploying to Azure App Service

In this article, we’ll explore several options for deploying an ASP .NET Core web app to Azure App Service in the cloud. From the infamous Right-Click-Publish to fully automated CI/CD, you’ll learn about the latest Deployment Center option in the Azure Portal for App Service for web apps.

NOTE: If you’re looking for information on deploying to Docker or Kubernetes, please check out the following docs instead:

 

Right-Click Publish (aka Friends Don’t Let Friends Right-Click Publish)

If you’ve made it this far, you may be thinking one of the following:
a. “Hey, this is how I deploy my web apps right now!”
or
b. “Hey wait a minute, I’ve heard that you should never do this!”

Well, there is a time and place for right-click publish. There have been many debates on this, so I won’t go into the details, but here are some resources for you to see what others are saying:

Tweet from Geoffrey Huntley: https://twitter.com/GeoffreyHuntley/status/994345821276536832

So, what’s a web developer to do? To quote from the aforementioned MSDN article, “Continuing with the theme of prototyping and experimenting, right click publish is the perfect way for existing Visual Studio customers to evaluate Azure App Service (PAAS). By following the right click publish flow you get the opportunity to provision new instances in Azure and publish your application to them without leaving Visual Studio:”

In other words, you can use this approach for a quick test or demo, as shown in the screenshots below for Visual Studio.

  1. Right-click your ASP .NET Core web app project in Solution Explorer and select Publish.
  2. Click the Start button on the screen that appears and follow the onscreen instructions.
  3. Ensure that you’re logged in to the correct Azure subscription account you want to publish to.

VS2017-RightClick-Publish

Right-click Publish your project from Solution Explorer

Follow the onscreen instructions

Follow the onscreen instructions

Continue reading