IaaS options are great when you need access to the host machine to configure and deploy your app, but it leaves you with a lot of management overhead. Platform-as-a-Service (PaaS) takes care of that for you, simplifying deployment and updates, provided your application is supported in the PaaS environment. Azure has a few PaaS options - App Service is a very popular one.
In this lab you’ll create an App Service deployment by pushing source code from your local machine. Azure will compile and configure the app for you.
Create a new resource in the Portal - search for Web app (which is one of the App Service types):
As usual, we won’t create from the Portal, we’ll switch to the CLI.
Create a Resource Group for the lab:
az group create -n <your-name>-labs-appservice -l eastus --tags courselabs=azure
Before we can create the app we need an App Service Plan - which is an abstraction of the infrastructure needed to run applications.
📋 Create an App Service Plan using the basic B1 SKU, and with one instance.
This is fairly straightforward:
az appservice plan create -g <your-name>-labs-appservice -n <your-name>-app-service-01 --sku B1 --number-of-workers 1
Open the RG in the Portal. The only resource is the App Service Plan. Open that and you’ll see an empty app list, and the scale up and scale out options (which are limited by the plan SKU).
We can create a Web App using the new App Service Plan. List the available runtimes to see what platforms are supported:
az webapp list-runtimes
Under the Windows options we have dotnet:6. This would work for pretty much any older .NET applications, and is a good fit for migrating apps to the cloud if you have the source code and you don’t need the control you get with IaaS.
📋 Create web app in the service plan using the dotnet:6 runtime, and set for deployment from a local Git repository.
Check the help text for a new web app:
az webapp create --help
You need to specify the runtime, deployment method and a unique DNS name for the app:
az webapp create -g <your-name>-labs-appservice --plan <your-name>-app-service-01 --runtime 'dotnet:6' --name <dns-unique-app-name>
Check the RG again in the Portal when your CLI command has completed.
Now the web app is listed as a separate resource - the type is App Service - but you can navigate to the plan from the app and vice versa
Open the web app and you’ll see it has a public URL, which uses the application name you set; HTTPS is provided by the platform.
Browse to your app URL, you’ll see a landing page saying “Your web app is running and waiting for your content”.
subscription="b214611b-9a79-4e7e-afb0-3d9785737f10" # add subscription here
az account set -s $subscription # ...or use 'az login'
# Create a single database and configure a firewall rule
# Variable block
let "randomIdentifier=$RANDOM*$RANDOM"
location="East US"
resourceGroup="<your-name>-labs-appservice"
tag="create-and-configure-database"
server="sb-azuresql-server-$randomIdentifier"
database="sbazuresqldb$randomIdentifier"
login="azureuser"
password="Admin@1234567"
# Specify appropriate IP address values for your environment
# to limit access to the SQL Database server
startIp=0.0.0.0
endIp=255.255.255.255
echo "Using resource group $resourceGroup with login: $login, password: $password..."
echo "Creating $resourceGroup in $location..."
az group create --name $resourceGroup --location "$location" --tags $tag
echo "Creating $server in $location..."
az sql server create --name $server --resource-group $resourceGroup --location "$location" --admin-user $login --admin-password $password
echo "Configuring firewall..."
az sql server firewall-rule create --resource-group $resourceGroup --server $server -n AllowYourIp --start-ip-address $startIp --end-ip-address $endIp
echo "Creating $database on $server..."
az sql db create --resource-group $resourceGroup --server $server --name $database --sample-name AdventureWorksLT --edition Basic --capacity 5
CREATE USER applicationUser with PASSWORD = 'App1234567'
GRANT select, insert, update, delete to applicationUser
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.1.2" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.10" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.9" />
</ItemGroup>
Add an “images” folder in wwwroot. Upload - cookies image
Update the home page to - index.html
Scaffold the DB context - run the below command in the visual studio package manager console:
Scaffold-DbContext "<your conn string>" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -ContextDir "Data" -DataAnnotations
Select the model (Cookie) and data context class from the dropdown
In DBContext.cs
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("ibdb");
optionsBuilder.UseSqlServer(connectionString);
}
In Program.cs
var connectionString = builder.Configuration.GetConnectionString("ibdb");
builder.Services.AddDbContext<<your-db-context-class>>(x => x.UseSqlServer(connectionString));
In appsettings.json
"ConnectionStrings": {
"ibdb": "Server=tcp:sb-azuresql-server-286930812.database.windows.net,1433;Initial Catalog=<your-database-name>;Persist Security Info=False;User ID=azureuser;Password=Admin@1234567;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
}
# Variable block
let "randomIdentifier=$RANDOM*$RANDOM"
location="East US"
resourceGroup="<your-rg-name>"
tag="create-manage-cache"
cache="ibcache-$randomIdentifier"
sku="basic"
size="C0"
# Create a resource group
echo "Creating $resourceGroup in "$location"..."
az group create --resource-group $resourceGroup --location "$location" --tags $tag
# Create a Basic C0 (256 MB) Redis Cache
echo "Creating $cache"
az redis create --name $cache --resource-group $resourceGroup --location "$location" --sku $sku --vm-size $size
# Get details of an Azure Cache for Redis
echo "Showing details of $cache"
az redis show --name "$cache" --resource-group $resourceGroup
# Retrieve the hostname and ports for an Azure Redis Cache instance
redis=($(az redis show --name "$cache" --resource-group $resourceGroup --query [hostName,enableNonSslPort,port,sslPort] --output tsv))
# Retrieve the keys for an Azure Redis Cache instance
keys=($(az redis list-keys --name "$cache" --resource-group $resourceGroup --query [primaryKey,secondaryKey] --output tsv))
# Display the retrieved hostname, keys, and ports
echo "Hostname:" ${redis[0]}
echo "Non SSL Port:" ${redis[2]}
echo "Non SSL Port Enabled:" ${redis[1]}
echo "SSL Port:" ${redis[3]}
echo "Primary Key:" ${keys[0]}
echo "Secondary Key:" ${keys[1]}
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.3" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.12" />
builder.Services.AddStackExchangeRedisCache(option =>
{
option.Configuration = builder.Configuration.GetConnectionString
("ibcache");
option.InstanceName = "master";
});
private readonly IDistributedCache _cache;
public CookiesController(sbazuresqldb286930812Context context, IDistributedCache cache)
{
_context = context;
_cache = cache;
}
public async Task<IActionResult> Index()
{
List<Cookie> cookies;
var cachedCookies = _cache.GetString("cookieList");
if (!string.IsNullOrEmpty(cachedCookies))
{
cookies = JsonConvert.DeserializeObject<List<Cookie>>(cachedCookies);
}
else
{
cookies = _context.Cookies.ToList();
_cache.SetString("cookieList", JsonConvert.SerializeObject(cookies));
}
return View(cookies);
}
Create an Azure AD app registration with secret value: w5R8Q~yEPejsgL1aJ4ubYVa5v0.Y-jWOHHdXqcJC
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "datacouchtrainingoutlook.onmicrosoft.com",
"TenantId": "87b7955d-4a78-474a-8a5c-6a5aaebe4ef2",
"ClientId": "d925f687-b7c8-49c4-bc17-13dab709e78c",
"CallbackPath": "/signin-oidc"
}
<PackageReference Include="Microsoft.Identity.Web" Version="1.1.0" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="1.1.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.9" />
Update the below builder services:
builder.Services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.Secure = CookieSecurePolicy.Always;
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
In the configure method:
app.UseCookiePolicy();
app.UseAuthentication();
Add _LoginPartial.cshtml to the views -> shared directory
@using System.Security.Principal
<ul class="navbar-nav">
@if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<span class="navbar-text text-dark">Hello @User.Identity.Name!</span>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignOut">Sign out</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in</a>
</li>
<li><a asp-area="" asp-controller="Account" asp-action="Register">Register</a></li>
}
</ul>
Add partial layout in the _Layout.cshtml file for sign in/out:
<partial name="_LoginPartial" />
Deploy the app using Visual Studio IDE. Right click on the web app in Visual Studio and click publish. Follow the authentication steps to deploy to the above app service.
Sample scaffolding Commands - Do not run directly
dotnet ef dbcontext scaffold "Server=tcp:sb-azuresql-server-856307307.database.windows.net,1433;Initial Catalog=paassql;Persist Security Info=False;User ID=azureuser;Password=Admin@1234567;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" Microsoft.EntityFrameworkCore.SqlServer -o Models --context-dir Context --data-annotations
Scaffold-DbContext "Server=tcp:ibdbserver.database.windows.net,1433;Initial Catalog=ibdb;Persist Security Info=False;User ID=adminuser;Password=Admin1234567;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
Scaffold-DbContext "Server=tcp:ibdbserver.database.windows.net,1433;Initial Catalog=ibdb;Persist Security Info=False;User ID=adminuser;Password=Admin1234567;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -ContextDir "Data" -DataAnnotations |