Skip to content

Commit b644c7d

Browse files
authored
✨ Implement Azure App Service and SQL Server configuration in AppHost (#202)
🔥 Merged without review This pull request introduces significant improvements to Azure deployment support and infrastructure configuration, along with some minor documentation and naming updates. The main changes include adding Azure-specific packages and resources, refactoring the distributed application setup to use the Aspire Azure hosting model, and updating naming conventions for database connection strings and contexts. **Azure Deployment & Infrastructure Enhancements:** * Added Azure-specific package references (`Aspire.Hosting.Azure.ApplicationInsights`, `Aspire.Hosting.Azure.AppService`, `Aspire.Hosting.Azure.Sql`) to both `Directory.Packages.props` and `tools/AppHost/AppHost.csproj` to enable Azure resource provisioning. [[1]](diffhunk://#diff-5baf5f9e448ad54ab25a091adee0da05d4d228481c9200518fcb1b53a65d4156R6-R8) [[2]](diffhunk://#diff-cda82b6742843f6c1767221e433612d9fa582a55058b1f5ee6a5563151ba4656R13-R15) * Refactored the distributed application setup in `tools/AppHost/AppHost.cs` to use Azure App Service, Azure SQL Server, Application Insights, and Log Analytics, including configuration for local and publish modes. * Implemented a new extension method `WithDropDatabaseCommand` for Azure SQL databases in `tools/AppHost/Commands/SqlServerCommandExt.cs`, replacing the previous implementation for local SQL Server databases and supporting Azure-native operations. [[1]](diffhunk://#diff-0c530e56451cfeca684c61f595b381fa8f2e1df518c3e435e1a7f03f8720599bR1-R30) [[2]](diffhunk://#diff-f7dbe3d8251b808d699d5786c9229041916af26d475bf23b34311ee77b385f3eL1-L40) * Removed the old program setup in `tools/AppHost/Program.cs` in favor of the new Azure-oriented configuration. **Naming & Consistency Updates:** * Standardized database connection string and context names from `"app-db"` to `"AppDb"` across infrastructure registration and test configuration files for consistency with Azure resource naming. (`src/WebApi/Common/Persistence/DependencyInjection.cs`, `tests/WebApi.IntegrationTests/Common/Infrastructure/Web/WebApiTestFactory.cs`, `tools/MigrationService/Program.cs`) [[1]](diffhunk://#diff-87636dbcff301e838a1382effcf2568766855b5a0cd02f8d46e12b3ae66939ceL16-R16) [[2]](diffhunk://#diff-50cf933f50db73c5b021cfdf9e67b3ed988d49aa11fcc492be8f2a94d2daeff3L31-R31) [[3]](diffhunk://#diff-b89afcf1c319e2f2497507ca4d2a43d2ac1633c94c1ac7e042ee454fcbe8b1d6L24-R24) **Documentation Improvements:** * Updated `README.md` to include detailed Azure deployment instructions using the Azure Developer CLI (AZD), and improved formatting for notes and versioning information. [[1]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L173-L175) [[2]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L211-R254) These changes collectively modernize the project's deployment workflow, making it easier to provision and manage resources in Azure while ensuring consistency and clarity throughout the codebase and documentation.
1 parent 10d07f2 commit b644c7d

File tree

10 files changed

+159
-71
lines changed

10 files changed

+159
-71
lines changed

Directory.Packages.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<ItemGroup>
6+
<PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="9.5.1" />
7+
<PackageVersion Include="Aspire.Hosting.Azure.AppService" Version="9.5.1-preview.1.25502.11" />
8+
<PackageVersion Include="Aspire.Hosting.Azure.Sql" Version="9.5.1" />
69
<PackageVersion Include="FastEndpoints.Swagger" Version="7.0.1" />
710
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
811
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10">

README.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,52 @@ To speed up development there is a `dotnet new` template to create a full Vertic
170170
dotnet run
171171
```
172172

173-
> **NOTE:** The first time you run the solution, it may take a while to download the docker images, create the DB, and seed the data.
173+
> [!NOTE] The first time you run the solution, it may take a while to download the docker images, create the DB, and seed the data.
174+
175+
4. Open https://localhost:7255/swagger in your browser to see it running ️🏃‍♂️
176+
177+
## Deploying to Azure
178+
179+
The template can be deployed to Azure via
180+
the [Azure Developer CLI (AZD)](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows,brew-mac,script-linux&pivots=os-mac).
181+
This will setup the following:
182+
183+
- Azure App Services: API + MigrationService
184+
- Azure SQL Server + Database: Data storage
185+
- Application Insights + Log Analytics: For monitoring and logging
186+
- Managed Identities: For secure access to Azure resources
187+
- Azure Container Registry: For storing Docker images
188+
189+
### Steps to Deploy
190+
191+
1. Authenticate with Azure
192+
193+
```bash
194+
azd auth login
195+
```
196+
197+
2. Initialize AZD for the project
198+
199+
```bash
200+
azd init
201+
```
202+
203+
3. Update environment variables
204+
205+
```bash
206+
azd env set ASPNETCORE_ENVIRONMENT Development
207+
```
208+
209+
4. Deploy to Azure
210+
211+
```bash
212+
azd up
213+
```
214+
215+
> [!NOTE]
216+
> `azd up` combines `azd provision` and `azd deploy` commands to create the resources and deploy the application. If running this from a CI/CD
217+
> pipeline, you can use `azd provision` and `azd deploy` separately in the appropriate places.
174218

175-
4. Open https://localhost:7255/scalar/v1 in your browser to see it running ️🏃‍♂️
176219

177220
## 🎓 Learn More
178221

@@ -208,7 +251,7 @@ Template will be published to NuGet.org when changes are made to `VerticalSliceA
208251
3. `package` GitHub Action will run and publish the new version to NuGet.org
209252
4. Create a GitHub release to document the changes
210253

211-
> **NOTE:** We are now using CalVer for versioning. The version number should be in the format `YYYY.M.D` (e.g. `2024.2.12`).
254+
> [!NOTE] We are now using CalVer for versioning. The version number should be in the format `YYYY.M.D` (e.g. `2024.2.12`).
212255

213256
## 🤝 Contributing
214257

src/WebApi/Common/Persistence/DependencyInjection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static void AddInfrastructure(this IHostApplicationBuilder builder)
1313
services.AddScoped<DispatchDomainEventsInterceptor>();
1414
services.AddSingleton(TimeProvider.System);
1515

16-
builder.AddSqlServerDbContext<ApplicationDbContext>("app-db",
16+
builder.AddSqlServerDbContext<ApplicationDbContext>("AppDb",
1717
null,
1818
options =>
1919
{

tests/WebApi.IntegrationTests/Common/Infrastructure/Web/WebApiTestFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
2828
// x.Services.AddSingleton<ILoggerProvider>(new XUnitLoggerProvider(Output));
2929
});
3030

31-
builder.UseSetting("ConnectionStrings:app-db", _dbConnection.ConnectionString);
31+
builder.UseSetting("ConnectionStrings:AppDb", _dbConnection.ConnectionString);
3232
}
3333
}

tools/AppHost/AppHost.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using AppHost.Commands;
2+
using Azure.Provisioning;
3+
using Azure.Provisioning.AppService;
4+
using Projects;
5+
6+
var builder = DistributedApplication.CreateBuilder(args);
7+
8+
// Configure the Azure App Service environment
9+
builder.AddAzureAppServiceEnvironment("plan").ConfigureInfrastructure(infra =>
10+
{
11+
var plan = infra.GetProvisionableResources()
12+
.OfType<AppServicePlan>()
13+
.Single();
14+
15+
plan.Sku = new AppServiceSkuDescription
16+
{
17+
Name = "B1", // Basic tier, 1 core
18+
};
19+
});
20+
21+
var sqlServer = builder
22+
.AddAzureSqlServer("sql")
23+
.RunAsContainer(container =>
24+
{
25+
// Configure SQL Server to run locally as a container
26+
container.WithLifetime(ContainerLifetime.Persistent);
27+
28+
// Use SQL Server 2022 as the default of SQL Server 2025 doesn't work on Linux/MacOS
29+
container.WithImage("mssql/server:2022-latest");
30+
31+
// If desired, set SQL Server Port to a constant value
32+
//container.WithHostPort(1800);
33+
});
34+
35+
var db = sqlServer
36+
.AddDatabase("AppDb", "app-db")
37+
.WithDropDatabaseCommand();
38+
39+
var migrationService = builder.AddProject<MigrationService>("migrations")
40+
.PublishAsAzureAppServiceWebsite((_, site) =>
41+
{
42+
const string envNetCoreEnvironment = "ASPNETCORE_ENVIRONMENT";
43+
44+
// Needed for hosted service to run
45+
site.SiteConfig.IsAlwaysOn = true;
46+
47+
// Dynamically set environment, so we can enable seeding of data (only happens in 'development')
48+
var environment = Environment.GetEnvironmentVariable(envNetCoreEnvironment);
49+
if (string.IsNullOrWhiteSpace(environment))
50+
return;
51+
52+
var envSetting = new AppServiceNameValuePair { Name = envNetCoreEnvironment, Value = environment };
53+
site.SiteConfig.AppSettings.Add(new BicepValue<AppServiceNameValuePair>(envSetting));
54+
})
55+
.WithReference(db)
56+
.WaitFor(sqlServer);
57+
58+
var api = builder
59+
.AddProject<WebApi>("api")
60+
.WithExternalHttpEndpoints()
61+
.WithReference(db)
62+
.WaitForCompletion(migrationService);
63+
64+
// Configure Application Insights and Log Analytics only if in publish mode
65+
// When running locally, use Aspire Dashboard instead
66+
if (builder.ExecutionContext.IsPublishMode)
67+
{
68+
var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("log-analytics");
69+
var insights = builder.AddAzureApplicationInsights("insights", logAnalytics);
70+
api.WithReference(insights);
71+
migrationService.WithReference(insights);
72+
}
73+
74+
builder.Build().Run();

tools/AppHost/AppHost.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="Aspire.Hosting.AppHost" />
13+
<PackageReference Include="Aspire.Hosting.Azure.ApplicationInsights" />
14+
<PackageReference Include="Aspire.Hosting.Azure.AppService" />
15+
<PackageReference Include="Aspire.Hosting.Azure.Sql" />
1316
<PackageReference Include="Aspire.Hosting.SqlServer" />
1417
<PackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" />
1518
</ItemGroup>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Aspire.Hosting.Azure;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace AppHost.Commands;
5+
6+
public static class SqlServerDatabaseCommandExt
7+
{
8+
public static IResourceBuilder<AzureSqlDatabaseResource> WithDropDatabaseCommand(
9+
this IResourceBuilder<AzureSqlDatabaseResource> builder)
10+
{
11+
builder.WithCommand(
12+
"drop-database",
13+
"Drop Database",
14+
async _ =>
15+
{
16+
var connectionString = await builder.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None);
17+
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
18+
19+
var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
20+
optionsBuilder.UseSqlServer(connectionString);
21+
var db = new DbContext(optionsBuilder.Options);
22+
await db.Database.EnsureDeletedAsync();
23+
24+
return CommandResults.Success();
25+
},
26+
null); // Intentionally using 'null' for the command state resolver as this command does not require health status checks. Downstream code is expected to handle 'null' appropriately.
27+
28+
return builder;
29+
}
30+
}

tools/AppHost/Extensions/SqlServerCommandExt.cs

Lines changed: 0 additions & 40 deletions
This file was deleted.

tools/AppHost/Program.cs

Lines changed: 0 additions & 25 deletions
This file was deleted.

tools/MigrationService/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
builder.Services.AddScoped<ICurrentUserService, MigrationUserService>();
2222
builder.Services.AddSingleton(TimeProvider.System);
2323

24-
builder.AddSqlServerDbContext<ApplicationDbContext>("app-db",
24+
builder.AddSqlServerDbContext<ApplicationDbContext>("AppDb",
2525
null,
2626
options =>
2727
{

0 commit comments

Comments
 (0)