From 5b8ca5159831e4fcd19daf1f10f6e6531db064e5 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:25:22 -0600 Subject: [PATCH 01/31] Add WithBun() documentation to JavaScript integration page (#289) * Initial plan * Add WithBun() documentation to JavaScript integration page Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> * Update Bun example to use specific version tag Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --- .../integrations/frameworks/javascript.mdx | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/content/docs/integrations/frameworks/javascript.mdx b/src/frontend/src/content/docs/integrations/frameworks/javascript.mdx index 81701518..42d642ca 100644 --- a/src/frontend/src/content/docs/integrations/frameworks/javascript.mdx +++ b/src/frontend/src/content/docs/integrations/frameworks/javascript.mdx @@ -18,7 +18,7 @@ import jsIcon from '@assets/icons/javascript.svg'; data-zoom-off /> -The Aspire JavaScript/TypeScript hosting integration enables you to orchestrate JavaScript applications alongside your Aspire projects in the AppHost. This integration provides a unified approach to running JavaScript applications with support for multiple package managers (npm, yarn, pnpm), runtimes (Node.js), and build tools (Vite, and more). +The Aspire JavaScript/TypeScript hosting integration enables you to orchestrate JavaScript applications alongside your Aspire projects in the AppHost. This integration provides a unified approach to running JavaScript applications with support for multiple package managers (npm, yarn, pnpm, bun), runtimes (Node.js), and build tools (Vite, and more). :::tip[Package rename] In Aspire 13.0, the `Aspire.Hosting.NodeJs` package was renamed to `Aspire.Hosting.JavaScript` to better reflect its broader support for JavaScript applications. @@ -106,7 +106,7 @@ var viteApp = builder.AddViteApp("vite-app", "./vite-app") - **Development script**: Runs the "dev" script (typically `vite`) during local development - **Build script**: Runs the "build" script (typically `vite build`) when publishing -- **Package manager**: Uses npm by default, but can be customized with `WithYarn()` or `WithPnpm()` +- **Package manager**: Uses npm by default, but can be customized with `WithYarn()`, `WithPnpm()`, or `WithBun()` The method accepts the same parameters as `AddJavaScriptApp`: @@ -183,6 +183,39 @@ var customApp = builder.AddJavaScriptApp("custom-app", "./custom-app") When publishing, Aspire uses `pnpm install --frozen-lockfile` if `pnpm-lock.yaml` exists, otherwise it uses `pnpm install`. +### Use Bun + +To use Bun as the package manager, call the `WithBun` extension method: + +```csharp title="C# — AppHost.cs" "WithBun" +var builder = DistributedApplication.CreateBuilder(args); + +var app = builder.AddViteApp("app", "./app") + .WithBun(); + +// Customize Bun with additional flags +var customApp = builder.AddViteApp("custom-app", "./custom-app") + .WithBun(installArgs: ["--frozen-lockfile"]); + +// After adding all resources, run the app... +``` + +When publishing, Aspire uses `bun install --frozen-lockfile` if `bun.lock` or `bun.lockb` exists, otherwise it uses `bun install`. + +Bun supports passing script arguments without the `--` separator, so commands like `bun run dev --port 3000` work without needing `bun run dev -- --port 3000`. + +When publishing to a container, `WithBun()` automatically configures a Bun build image (`oven/bun:1`) since Bun is not available in the default Node.js base images. To use a specific Bun version, configure a custom build image: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var app = builder.AddViteApp("app", "./app") + .WithBun() + .WithDockerfileBaseImage(buildImage: "oven/bun:1.1"); + +// After adding all resources, run the app... +``` + ## Customize scripts You can customize which scripts run during development and build: From 6e588ed4d543d242ea971e667432ada47b03e028 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:19:35 -0600 Subject: [PATCH 02/31] Add documentation for deployment slot support in Azure App Service (#290) * Initial plan * Add documentation for deployment slot support to Azure App Service Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --- .../cloud/azure/azure-app-service.mdx | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-app-service.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-app-service.mdx index ea4ffad2..ca82f238 100644 --- a/src/frontend/src/content/docs/integrations/cloud/azure/azure-app-service.mdx +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-app-service.mdx @@ -129,6 +129,67 @@ The preceding code: - Adds an application setting for `ASPNETCORE_ENVIRONMENT`. - Adds multiple tags for metadata and organization. +## Deployment slots + +[Deployment slots](https://learn.microsoft.com/azure/app-service/deploy-staging-slots) let you deploy your app to a staging environment for testing before swapping it into production. The `WithDeploymentSlot` extension method configures a deployment slot for the App Service environment: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +builder.AddAzureAppServiceEnvironment("appservice") + .WithDeploymentSlot("staging"); + +var apiService = builder.AddProject("apiservice") + .WithHttpHealthCheck("/health") + .WithExternalHttpEndpoints(); + +builder.AddProject("webfrontend") + .WithExternalHttpEndpoints() + .WithHttpHealthCheck("/health") + .WithReference(apiService) + .WaitFor(apiService); + +builder.Build().Run(); +``` + +The preceding code configures all App Service websites to be deployed to a `staging` slot. + +### Deployment slot behavior + +When you deploy to a slot, the deployment behavior depends on whether the production App Service already exists: + +- **New App Service**: If the production App Service (production slot) doesn't exist at the time of deployment, Aspire deploys to both the production App Service and the specified deployment slot. +- **Existing App Service**: If the production App Service already exists, Aspire deploys only to the specified deployment slot. + +This behavior allows you to safely deploy and test changes in a staging environment before swapping to production. + +### Using parameters for deployment slots + +You can also specify the deployment slot value as a parameter, allowing the slot name to be provided at deployment time: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var slotName = builder.AddParameter("deploymentSlot"); + +builder.AddAzureAppServiceEnvironment("appservice") + .WithDeploymentSlot(slotName); + +// Add your projects and other resources... + +builder.Build().Run(); +``` + +With this configuration, you can specify the slot name when running `aspire deploy` by providing a value for the `deploymentSlot` parameter. + +### Applying customizations to slots + + + +When you customize App Service resources using `PublishAsAzureAppServiceWebsite` or `ConfigureInfrastructure`, those customizations apply to the production slot by default. If you need the same customizations on your deployment slot, you must apply them separately. This gives you fine-grained control over the configuration of each slot, allowing staging and production environments to have different settings when needed. + ## Provisioning-generated Bicep If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by-hand, instead the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. From 7da78d675580b68ba5ee3fe5a73424dc2cc87248 Mon Sep 17 00:00:00 2001 From: IEvangelist Date: Sun, 25 Jan 2026 12:17:23 -0600 Subject: [PATCH 03/31] Add Azure integrations documentation and CLI command references - Created documentation for Azure Application Insights integration. - Created documentation for Azure Data Explorer (Kusto) integration. - Created documentation for Azure Log Analytics integration. - Created documentation for Azure Data Lake Storage integration. - Added `aspire doctor` command placeholder with redirect to main CLI command page. - Added documentation for `aspire ps` command to list running AppHost processes. - Added documentation for `aspire stop` command to stop running AppHost processes. --- run_git_commands.ps1 | 58 +++ src/frontend/config/sidebar/docs.topics.ts | 52 +-- .../config/sidebar/integrations.topics.ts | 16 + .../config/sidebar/reference.topics.ts | 2 + .../assets/icons/azure-dataexplorer-icon.svg | 1 + .../src/assets/icons/azure-datalake-icon.png | Bin 0 -> 9334 bytes .../src/content/docs/app-host/eventing.mdx | 114 +++++- .../src/content/docs/architecture/glossary.md | 25 -- .../docs/architecture/resource-examples.mdx | 118 +++--- .../aspirecontainershellexecution001.mdx | 46 +++ .../docs/diagnostics/aspiredotnettool.mdx | 44 +++ .../docs/diagnostics/aspireextension001.mdx | 49 +++ .../docs/diagnostics/aspireinteraction001.mdx | 38 +- .../docs/diagnostics/aspirepostgres001.mdx | 43 +++ .../src/content/docs/diagnostics/overview.mdx | 13 +- .../docs/extensibility/custom-resources.mdx | 292 +++++++++++++++ .../fundamentals/annotations-overview.mdx | 103 ++++++ .../docs/fundamentals/health-checks.mdx | 26 +- .../docs/fundamentals/networking-overview.mdx | 113 ++++++ .../docs/fundamentals/service-discovery.mdx | 77 +++- .../content/docs/get-started/aspire-sdk.mdx | 2 +- .../content/docs/get-started/first-app.mdx | 10 +- .../src/content/docs/get-started/glossary.mdx | 336 ++++++++++++++++++ .../docs/get-started/troubleshooting.mdx | 164 +++++++++ .../docs/get-started/what-is-aspire.mdx | 82 ++++- .../azure/azure-application-insights.mdx | 260 ++++++++++++++ .../cloud/azure/azure-data-explorer.mdx | 308 ++++++++++++++++ .../cloud/azure/azure-log-analytics.mdx | 157 ++++++++ .../cloud/azure/azure-sql-database.mdx | 105 ++++++ .../cloud/azure/azure-storage-blobs.mdx | 87 +++++ .../cloud/azure/azure-storage-datalake.mdx | 278 +++++++++++++++ .../cloud/azure/customize-resources.mdx | 8 +- .../docs/reference/cli/commands/aspire-ps.mdx | 85 +++++ .../reference/cli/commands/aspire-stop.mdx | 60 ++++ .../docs/reference/cli/commands/aspire.mdx | 2 + .../docs/reference/cli/configuration.mdx | 10 +- .../cli/includes/config-settings-table.md | 1 + src/frontend/src/data/integration-docs.json | 18 +- 38 files changed, 3068 insertions(+), 135 deletions(-) create mode 100644 run_git_commands.ps1 create mode 100644 src/frontend/src/assets/icons/azure-dataexplorer-icon.svg create mode 100644 src/frontend/src/assets/icons/azure-datalake-icon.png delete mode 100644 src/frontend/src/content/docs/architecture/glossary.md create mode 100644 src/frontend/src/content/docs/diagnostics/aspirecontainershellexecution001.mdx create mode 100644 src/frontend/src/content/docs/diagnostics/aspiredotnettool.mdx create mode 100644 src/frontend/src/content/docs/diagnostics/aspireextension001.mdx create mode 100644 src/frontend/src/content/docs/diagnostics/aspirepostgres001.mdx create mode 100644 src/frontend/src/content/docs/extensibility/custom-resources.mdx create mode 100644 src/frontend/src/content/docs/get-started/glossary.mdx create mode 100644 src/frontend/src/content/docs/get-started/troubleshooting.mdx create mode 100644 src/frontend/src/content/docs/integrations/cloud/azure/azure-application-insights.mdx create mode 100644 src/frontend/src/content/docs/integrations/cloud/azure/azure-data-explorer.mdx create mode 100644 src/frontend/src/content/docs/integrations/cloud/azure/azure-log-analytics.mdx create mode 100644 src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-datalake.mdx create mode 100644 src/frontend/src/content/docs/reference/cli/commands/aspire-ps.mdx create mode 100644 src/frontend/src/content/docs/reference/cli/commands/aspire-stop.mdx diff --git a/run_git_commands.ps1 b/run_git_commands.ps1 new file mode 100644 index 00000000..4dc0e4a7 --- /dev/null +++ b/run_git_commands.ps1 @@ -0,0 +1,58 @@ +# Change to the repository directory +Set-Location "E:\GitHub\aspire.dev" + +# 1. Stage all changes +Write-Host "===== Step 1: git add -A =====" +git add -A +if ($LASTEXITCODE -eq 0) { + Write-Host "Successfully staged all changes" +} else { + Write-Host "Error staging changes (exit code: $LASTEXITCODE)" +} + +# 2. Check status +Write-Host "`n===== Step 2: git status =====" +git status + +# 3. Commit with the provided message +Write-Host "`n===== Step 3: git commit =====" +$commitMessage = @" +Documentation coverage audit: CLI commands, diagnostics, and config updates + +## New CLI command documentation +- aspire-stop.mdx: Stop a running Aspire AppHost +- aspire-ps.mdx: List running Aspire AppHost processes + +## New diagnostic pages (experimental APIs) +- ASPIREINTERACTION001: Interaction service experimental API +- ASPIREDOTNETTOOL: .NET tool resource experimental API +- ASPIREPOSTGRES001: PostgreSQL MCP experimental API +- ASPIREEXTENSION001: Extension debugging experimental API +- ASPIRECONTAINERSHELLEXECUTION001: Container shell execution experimental API + +## Configuration updates +- Added missing polyglotSupportEnabled feature flag to config-settings-table.md +- Fixed stale feature flags in example settings.json + +## Navigation updates +- Updated sidebar with aspire ps and aspire stop commands +- Updated aspire.mdx commands table with new commands + +Note: aspire doctor command is covered by PR #270 +"@ + +git commit -m $commitMessage +if ($LASTEXITCODE -eq 0) { + Write-Host "Successfully committed changes" +} else { + Write-Host "Error during commit (exit code: $LASTEXITCODE)" +} + +# 4. Push changes +Write-Host "`n===== Step 4: git push =====" +git push +if ($LASTEXITCODE -eq 0) { + Write-Host "Successfully pushed changes" +} else { + Write-Host "Error during push (exit code: $LASTEXITCODE)" +} diff --git a/src/frontend/config/sidebar/docs.topics.ts b/src/frontend/config/sidebar/docs.topics.ts index dbbf52c7..e81f9a62 100644 --- a/src/frontend/config/sidebar/docs.topics.ts +++ b/src/frontend/config/sidebar/docs.topics.ts @@ -400,6 +400,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = { }, slug: 'get-started/configure-mcp', }, + { + label: 'Troubleshooting', + slug: 'get-started/troubleshooting', + }, ], }, { @@ -710,6 +714,28 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = { 'zh-CN': '资源注释', }, }, + { + label: 'Glossary', + slug: 'get-started/glossary', + translations: { + da: 'Ordliste', + de: 'Glossar', + en: 'Glossary', + es: 'Glosario', + fr: 'Glossaire', + hi: 'शब्दावली', + id: 'Glosarium', + it: 'Glossario', + ja: '用語集', + ko: '용어 사전', + 'pt-BR': 'Glossário', + 'pt-PT': 'Glossário', + ru: 'Глоссарий', + tr: 'Sözlük', + uk: 'Глосарій', + 'zh-CN': '术语表', + }, + }, ], }, { @@ -790,6 +816,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = { label: 'Extensibility', collapsed: true, items: [ + { + label: 'Custom resources', + slug: 'extensibility/custom-resources', + }, { label: 'Interaction service', slug: 'extensibility/interaction-service', @@ -1080,28 +1110,6 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = { }, slug: 'architecture/resource-examples', }, - { - label: 'Glossary', - translations: { - da: 'Ordbog', - de: 'Glossar', - en: 'Glossary', - es: 'Glosario', - fr: 'Glossaire', - hi: 'शब्दावली', - id: 'Kamus', - it: 'Glossario', - ja: '用語集', - ko: '용어집', - 'pt-BR': 'Glossário', - 'pt-PT': 'Glossário', - ru: 'Глоссарий', - tr: 'Sözlük', - uk: 'Глосарій', - 'zh-CN': '术语表', - }, - slug: 'architecture/glossary', - }, ], }, ], diff --git a/src/frontend/config/sidebar/integrations.topics.ts b/src/frontend/config/sidebar/integrations.topics.ts index 528e7a6f..d6e0e482 100644 --- a/src/frontend/config/sidebar/integrations.topics.ts +++ b/src/frontend/config/sidebar/integrations.topics.ts @@ -293,6 +293,10 @@ export const integrationTopics: StarlightSidebarTopicsUserConfig = { label: 'Azure App Service', slug: 'integrations/cloud/azure/azure-app-service', }, + { + label: 'Azure Application Insights', + slug: 'integrations/cloud/azure/azure-application-insights', + }, { label: 'Azure Cache for Redis', slug: 'integrations/cloud/azure/azure-cache-redis', @@ -305,6 +309,10 @@ export const integrationTopics: StarlightSidebarTopicsUserConfig = { label: 'Azure Cosmos DB', slug: 'integrations/cloud/azure/azure-cosmos-db', }, + { + label: 'Azure Data Explorer', + slug: 'integrations/cloud/azure/azure-data-explorer', + }, { label: 'Azure Event Hubs', slug: 'integrations/cloud/azure/azure-event-hubs', @@ -317,6 +325,10 @@ export const integrationTopics: StarlightSidebarTopicsUserConfig = { label: 'Azure Key Vault', slug: 'integrations/cloud/azure/azure-key-vault', }, + { + label: 'Azure Log Analytics', + slug: 'integrations/cloud/azure/azure-log-analytics', + }, { label: 'Azure PostgreSQL', collapsed: true, @@ -355,6 +367,10 @@ export const integrationTopics: StarlightSidebarTopicsUserConfig = { label: 'Azure Storage Blobs', slug: 'integrations/cloud/azure/azure-storage-blobs', }, + { + label: 'Azure Data Lake Storage', + slug: 'integrations/cloud/azure/azure-storage-datalake', + }, { label: 'Azure Storage Queues', slug: 'integrations/cloud/azure/azure-storage-queues', diff --git a/src/frontend/config/sidebar/reference.topics.ts b/src/frontend/config/sidebar/reference.topics.ts index 24cb36e5..3354c876 100644 --- a/src/frontend/config/sidebar/reference.topics.ts +++ b/src/frontend/config/sidebar/reference.topics.ts @@ -281,11 +281,13 @@ export const referenceTopics: StarlightSidebarTopicsUserConfig = { ], }, { label: 'aspire new', slug: 'reference/cli/commands/aspire-new' }, + { label: 'aspire ps', slug: 'reference/cli/commands/aspire-ps' }, { label: 'aspire publish', slug: 'reference/cli/commands/aspire-publish', }, { label: 'aspire run', slug: 'reference/cli/commands/aspire-run' }, + { label: 'aspire stop', slug: 'reference/cli/commands/aspire-stop' }, { label: 'aspire update', slug: 'reference/cli/commands/aspire-update', diff --git a/src/frontend/src/assets/icons/azure-dataexplorer-icon.svg b/src/frontend/src/assets/icons/azure-dataexplorer-icon.svg new file mode 100644 index 00000000..59d2fd75 --- /dev/null +++ b/src/frontend/src/assets/icons/azure-dataexplorer-icon.svg @@ -0,0 +1 @@ +Icon-analytics-145 diff --git a/src/frontend/src/assets/icons/azure-datalake-icon.png b/src/frontend/src/assets/icons/azure-datalake-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3aadfd0cb6dcf9d4d85e5bd906e253ad857b0aee GIT binary patch literal 9334 zcmcI}2UL?yw=N)Eq>2hsVkk=QRZ4&mLhntQKthMmOE7?_bm>S3r767$NEhkSq=Pgm z(u+VS0)ZR+|L;HFIqUxC-m})dSu1Pach;U~p4ofP?05DK*V0hBMM_VKgM)KR84A|H z!NHBi{z1fq*q-?3oXgmT&QsCIQx{?5>0{{*$B}~}tl$7;XG>eS4%`yv=hh3C!oj%# zwAVB8G*VZSv_?4dT3*ZW`Z~K}N8{i~$@;olT06o$0akEZdlzZoUSlf|U=Nc98j7g% ztGg<|?d+jQcepN6L(dxNXe|K)%E|zwd?m2}&TvmlfUmQYi-)AIH1H3uB=-5bn-2*1 zBjV{O4g3cwBXuo+0>T{*5aH$LvE~;Q0Ei0n3JCK{2upAS1o;K{`2+;{_{Dex_$5We zB>4pZ|NH^5v$?};Bz3@$f6j${lLp#(db&#T@%i}p@cIbxBHV5H1SBLR`1l3+1O<7p z5YyLfn7x>&=N!O}o1 z0+v9SLb ztN3rnUb}*`D|Tiu+}++A4uiNOoB@B9ENTDmxyb)pynl{`{d+D7|8^`NmJHvuVgF|6 zzq+vAbN%=)x5d8v%kkkZSZ{a7y0w^D6g3VGU{e_^r|0{9d(O|(bUgFN{(^sv14rq^ zlA`$vs*_N%OY~{jgB0>ue%ujCJ6FNj=PwHqQnNe-6Fkpj)Eb619%SrhP^N5XKsAQc zY@o||8~B<&MECHz+McR8S-*t)jq(<&=oB8qcX^7O{3Z?b%ZpmFJvO0loRd6@(Jc*o zEhk^0>4> zNaV?S54LV-U#;F?5>b^x+{4F{2SxlMYJAdcoE{LEO252{KHnO@U)M1HA*Pfoe(wmy zn1uG&7)N(M+r;>cUaiC;Ta#@Ga328h@vUucY&uudx5BM$D3KFdeZ*$2{lOd4YBq!( z!YF@3j9(ZP5P<(O==Kc7`l{!>Ra+Yhhb9*otV(dAaV$MHtMkK&7mU33cjFicL`;lZ zIZLrbn@R_kynQ(SiF<9@GQ7J$VMuSpa#&l!A#K$K5)wp7i5H)2^da3mYCeO!lQ4^b z;J5g$dMMlnS#ZFKi;HD7`um1Ksr9>-ZpV?vT=Y3yYE$vtcWdq`G80~h%<$5SrlQ1x z2+lKq@Y}4_Z*G)fyy>lw$z>$%C|DxXRfc z?_TV49378l*gof$nKaiLD$g$V@wxq_p5qTfJydvXXLNH;P1xv^PynmamqDiJP`Ps3 zXHSNN$0j{(Iiv|+TqZ2t$`IeOmR3Nh0{!+q)`9y0B;6F68v%^9cid)HO%E3Oi>7@i zvZZ$+_9ah@90dG~n|~J~&37wC8c1ej@N^DvhaKrR2kwsVP(;`l2rz6XU&gglN0avz z4rAgI!%kTc*>pY1zbAv%-ezMpQ0cXAL!aEz87Utrt@^s zI6B$Ihmfv6JNn$%vD~2ZmEpCHNhOqCDl)IA1gnW4j7~-j`pLs zs6Kc_EVM;w)*{AA>6~oTWVT(4n+5Q@t?t|5QmgXHI$v1dj7{Vx&l~eV@=7CtK|HW|E$KUGUlYn_iL!MRfj<|`KNr& z{6#);{_0V>A&t$95-36tuqoiDHP>R3boAOziktbk3D_JU(x}x|k!~jSOOf?X_5+u0 z$-?4KDtAO@pQd^xp;PxHS!H`5(5Yjp*c?(Q(r|p#;8R|hdE@%M)0$KH$0QIE!p`Lu zxz$PMqSK*QDN5NAlA;eoj?cmjhoFhtpNIz+LV$sl+xhcrE%}R;sEa! z!6cU2>jebggq}(2FAQs5ng(Q^@y}q)WT^t}h#KGXm!jj$G-lMRkjL#3vT8;cZj1_s zd+u@SH1V}>YT;^IH|u2!C-t?vQ+V$t=Dv`2NGZJI5lU>u0mB$740WYu?%^`sz zlgBBJ5YU%5Ub1dcP3OnhFbwhX)S~b^MsSt8mtUDOWX*`p@*)_|tp}%r*f#o#N>YE% zWn>uUSa4`t9Iv@kSz>L=eLvQ;Q-L|6u^6LFck7Bd`a|%~>49|k@)raQL-}JL{L+~j zw-N?&r=>vEo<4raOcN^c<;L@1s+bwL{h_XLtJSZcz-t#Z5(I>Umzb+CML}n#(HaZc zLR+A=A~#vit-F^qreIk6tFrvgYdKy3C)1f?R@T>5RLjJ%1sf%XnM{T>Yd+r#6Tf3D zO{LA;VU@PJMuI&wJwane=5aEltd60ZEf;Os%B;u08!IL?O9s*nx} zj1!L^=&QOqXgPs(JKM-rxQ1DJ#z9-fdfK8@AZIfESA9vv2^b|WFFK}Nv=uVb77E{CDA zFQVe9qmJR{ysJWA@jE!H&R`|3qWoVTakwaDjFX&k9~z!^wnIf@MgH3vJ*Ndu#0!vZft&G_g(h85sJ{ zQ2iyysOJ!6SBbJ@Qd@(-hY7w?yydNzXUdTDpq0#_wZ1b{N#;47&4WiI#kSH}N9V-& z!DMLRN+EpR(S>YGZXP`nTz#DFWq&ilSaE8SA}UaLr@LYrk7$TN<7+T*IP zr05Tep;4>oEv{0k2p5)h|3;DMJB#%5kkqqA}5YQ~M$EaxRI{IQyM#{*k zO6$;B)XAKVwszdn`Po!3(aN}J_RAbu&P_VGM|YH)nKfDUaO4YHZx#~c>ywcJT{pL+ zP8_9a>AMj>qUkHsnVTzglB%T`S(4M9e;+dmor>^2o@Sloxr~bl<9T@Ez5eZis40F2 zeUo|l_FSW3S@ZGtjM;@%wwS)H(77dwtG9q|F3MY~+*!8+g?%L{<9v;pkCHAt>{*dE zWKD}VuhKcY$!MufIQbbS<9bUB+Y#n zUu6)0XFjf5N=!|2Dm7neJKaGkqf^r4UXD4*?^%&{ec8Mt?sy}-moA#zRG7b}jGd!e zB7b&KZ3oC@s9kxGmO5H1C(3=nClMLyowxi*6NqQUv zy>KuwtkhxIw-;i(=R*G)4AUFnycmu>WibzDXOR+cm%dMa5^<6dvvHK-?yu-@t6)T% zomS&LoA)abKc-|69*PyGI(>)Z`@}tq?V)}-l$2Y3tEsjtGYglspXPRFF_Z>8i6#b@ zI^H$19Bu1|_2+U?A^QCCf(#@@#+|NhCF+Se{mA|)krYz@*S;A{ULLCX4L{valIP(y zwLkgth7-<^N1G_E6gJBNPRlR|I(f(e7K4AUfEOEzWWlB)LxH%eF(_%fJf z-GYg-gN!Dthh(^l4a?t%I8LlwwA>$eT1hCr(w=|3PcgD_UY26>bR=7~+=8r-E`)F8 zF2XKoPFqn!w&lHib7A`GM}25!v4(>wt;3t8+vm3_gvvbU9vBXyOuuG$`Tf2mY&Q2h z&uo_ZHl3nTd;p^nB6N)afoa;o8_{fh0!zjbNj*Yu!+F~Q~+$03k>AN1|eVJlPgS#MO; zlT_{9`X3~*;dy2beX?A|whP&luCS&3tz?Tg^Ye!m7T3G1kFZ09s<$7ik@20!8MBaJ zbWst}6^kYcF&g@uXv;o8d#i9dynO<|Q*)(pM|7T5!{2YdwN|wg8|f(fh&mjo=!$c% zf+BiN4!lnmpsf4k6hQW$CpMR$H^L>!6CwImLZ#Ii$a-Jw=YtLr1z<$=WwCs0Wht6P{}Jzh|St>*ultd-RgC0^)u7Rl=PYoR0R=3+wfVJ(-$rZW<5vM3HDL9h=}yH$d%83 zFiCv7VyI4_uKMWR$emiLy*geVOP6I#urIPzY}H!1r5@gCwS0Dh{T#b(3i>$X(x}el zzg?MO+Uz6gg`CN^{h%P(vR?TQ6}p9i>+vtjlGDH`ABXsa%^XP1p8XHF=4N0HxNXQB~xA)*l~!Oy-Tg2}P>@ zep{+OkMgnL1nSCR^*f9YHmS6FC3Uu*>sKFbAhUiqjvjr|1MT5zZ+kxKMn~#X^>~{B zSvMMWz_wITuN(KlRbTaW1h^(bF||5}wd-wNx#m35aVXE*kP7P!Xx!dUzgN?LVs{J2 z`cd3Q>@-`!#+=on5$MZ)kms#zKZz0f?B4a%7tz5JM#G%68kqRn=&@b)QX_8(_0G?q zgD!S`&fjBH8m_ek2MLxn?l$N_Rp1ao{j-W?5Nze*mX@T;EoQd?EFpC4s%;t=nnehareWfWQNBQ*f5t6J( zNR`8QQSjqX{7;G4keZp9>HZ6I!skyyO-ZTtfb}~|j1=&`s8ar_*t&;|lon!#W4eYCxNPI`I=6GqBx7?zvOX|ZxQdT%h&NIy2Nb)ws1 z1JL(hUu07mQ1ywg?~75>z%u8h$45t59%^`h9nMwaksigmz{?=D*cR$v4PIM1jrC{( z#^;42Hy1G&%*gn3QjLcwahO*np^m)yv;EF7_2SafBojolNDWTw(>6SNrxkoL)A^n- z3qnFYe)Mi(!b1sU1Rb$ZtWN54(}OT+Zd0+kXEhJG_Wt3PjhECP1;}7@sXlNxuq{kW}lqA;Lhjsv-4ET zprEHq*pMuBb#Z8mQe&R>`k7?-waM>LM|-&XHaQ)y+Swxx%)RrFkdCz@WlaRae5elx zS1#_p|GaX@{82svS&;KWM_0EUuep<~5V~%r9dHG8Fif0W<4H5q1^^yCUE4kT`puAL z!jS{1uDC}&YFSubZYyT=W_?bxy}g~9^p);A=m7UKnKB*%yx=c~-yO@%(0iVyX=!P& z#V@Z4->!d~+VQsGO@2p_s@}0&sG7NDriB!xitMfm`1vF#=q+F=2ksqdFJwEI7!ndP zc7Emzhr>59Rz`unB?@-KcEUBq$~W;}2qw;V3QGz0@3K=;K5gQyHQCldE5}>E5EPY4 zyL^nP+<(NyMbpmDe&Mw>2N9qRj;egn^C&N22L~@WK2}1`U2^uM{#>Gg>>g3V0~{Pm zN^J5K_pc@>2nUCl80T*d2!w<4Cu8%k1^~dp!NbG(Tl1eWuHXJM2KMd0U|4 z{cSD(Wc9bT{FBxHX-WTMj87j{w)M%r2-AD(2@zvG<+-mgPu#lKiORDh143*1(~O#` zM)&a39VWsX`1p$b#_W|xR}ivv^(C(I1lSy27mRc%L}8u_k@V93tISZ&ST-(R@Y3uL zGmpBk_v}XjoWS&56-9!CQ3DB!U{U`~RO^Nsh7LQAWH`YV+i=xVNeo-@CY#&%`+FJ6 z;y;~Ymj@fFWeTol*BoH3Adr?6x~%AQ{I2<7SY26CM~)+19trWx?1A~u*_)ShEeC$o z6^4RPEF2zf4Rzyjb@Hq}ZR43ibU*#W=E2JM4)tKN^H;XW0uF!9Edo{eOSDERF6r9}~TIS%)w9 z%2L$_i4Iw9xErXiUy)6l7Si3Ml6Y9=9n+-Utn-CkNEpwGHj5q@A08B;WM#9Jcj-T3 zwyzCNd-E$JKE~UmxqNBZZIH@vIp3CB61Y8h*7LU7+`BM!x^p5H1PX(;tP>paP-sdr zS+h{jU4S;fEyQPa7N)j0>T?Y}HuVvtn%*%sK2Wkf`jqyQ2Wy+>gaE*s*HEXeWK|bA zqdo7D{fu8O#gWj`@RHVC7@r??RZ?HNNO4|Zh9gsQ?%fHC6K>5+S)GVzJ?>ak(&3a0 zRV?0A%I3n`@&E>C%ynEL}nC)Mq{`WV+34rZf~-M%~JDLgO8W4Or~Xne5u&8r#e0<$i_*B5ThX8UW!gk`_5buJg;ifw)L}M5623#k@d0abZ)pBm7Jt&EO zMI|&@`&ASLHjF{#mk@jRZGY>{xvweR=fH)qs>}$w-PUE`46_zPnfFIytY!{{;-D23 zfx70+`b&Lc`4b%OlEu0wcP;!%BMuI7NZiY881G*E>=M?kjx#qb<-WfYdyvxlqg%I} zk+H;7+}TkH0GLR9&27XM-sNXCbHc~T-guMmC9>3dpj)>ZmnGXGl++#FMbsIv?PJwa zrwbc1@f{*)S7W60oRSu@t&WZyzCUxhnN%e{_K>Gd!;KY4AeM#n84s_T9@))k(>B1* z3)A;yZQ0jQE}3FoZJWFaWG!?pX_AY-HDUZry;Pv4EXk-(r7~Mwi~qr}P&AWa!pFw+ z1Ey$?jaN3!xaRlhVyEA^xOFNJ2xOUYehKZPpE6Sfsf4z4gpMm%b-HHUrEWGU#*xnn zRL~d@=Wcm7+S{Wm@fnlm+35S(L`XMHhq@UjB z&x4E*20CJ@OJ`BLNs|#&^tA0W#qv49OIuXA9#>0EBSL!_#vRh*fl0+ZWyRS_zsr;ele%QM z?q95NW~NBLe4b24^7$-H9^+rt^WG_7J9*kC1s>B!tY|sgI}?`oy#ly%a4444e;jUR=DQHAo1_3;5eL; zd_k%>wp^vwR_7V!%Zy06>w7DqJx*iZC|axBrNF z`t$TEex4n0>5=B3usjRVy<33CXhrIBv=4*Mz7a^uy&KBzC)dnz>#b$f`GU){14B(V zuA=OxVr|9#14~-jff^O(^GZyg);hy4EwGvGydT))#8rE8ac)ZwU;b=$9M#9H;;kL4 zDQEnSFJG4gjH#q94Nw(I%mp)#Q-|8F^ZPLDmCf(qDnN%JdX4OTaaP>NXs=tmmFe;z z?jAK(n5E}uv4`I;2S$R-X31I=;`bY!dBg&!$d13 zvG}32k$tGTsmz~IQn(yF>!l(yu=#wB)S#%EMZ~37dR!RhJa48t zUMVQ(Hn6!xfNOQDgn}ZVj$P_k^Rp&%qU$n7Jg$VJ0W}>aM11=s^0hGuZe@!El|==+ z=$U3UBnGqgtX1QW+{m@uLH|~^Iw7Q$b@$*_i2Zjz7;%{XSQlP+$a+uX(W36q#W2Q4 z#xbcp5=-L&VC~axXsdT8uuPMN2G8o1onCiHpwa!!D}=!_Wn-+tUj{|7uv3%DWOz1+ zVI_1nvAY7>sWyVuk+UdE?nxmZHu27xK9s8jDv-BGF5=i_E{z~K_O-ffEx&n$nfhzM zi!s{gEQ6(xN4}aPE{)pTWlxyY@!TbZte&Slq;fiWspKsj+K`nss2cgF0!5lAlhX%< zxQGE4XF3;8n{~)ZaV2sknGEWl+WJX-D&`eNzvyN@9c#VQchZG*Gs;-*;k!Pb;#QN; zN2lkT=}j7@@IDf#+bG{nllwP+_bo3{^jYJIC^BN;0tDAdExh;zcqwk#$@;n|aObdhMiuc9>$mpQLFDn z>GChmBmLykuT>PBF$5cwBHxE^R@%u_yYZLt$A?40yEQCe?oGH2Rl|(cbjs&oyc~TDa&=hUj*DwFelGeqd}>=^wXO?B(w1 ecT6BIA

YfC;(9pL{sjpR%F`xJ>?O@c#lw9GlYs literal 0 HcmV?d00001 diff --git a/src/frontend/src/content/docs/app-host/eventing.mdx b/src/frontend/src/content/docs/app-host/eventing.mdx index 68f93337..9b37f131 100644 --- a/src/frontend/src/content/docs/app-host/eventing.mdx +++ b/src/frontend/src/content/docs/app-host/eventing.mdx @@ -1,9 +1,10 @@ --- title: AppHost eventing APIs -description: Learn how to use the Aspire AppHost eventing features. +description: Learn how to use the .NET Aspire AppHost eventing features for lifecycle events, custom event publishing, and event-driven resource orchestration. --- import { Aside, Steps } from '@astrojs/starlight/components'; +import LearnMore from '@components/LearnMore.astro'; In Aspire, eventing allows you to publish and subscribe to events during various AppHost life cycles. Eventing is more flexible than life cycle events. Both let you run arbitrary code during event callbacks, but eventing offers finer control of event timing, publishing, and provides supports for custom events. @@ -301,3 +302,114 @@ The subscriber approach keeps builder code minimal while still letting you respo - You can register handlers for any built-in event (AppHost or resource) or for your own custom `IDistributedApplicationEvent` types. Use this pattern whenever you previously relied on `IDistributedApplicationLifecycleHook`. The lifecycle hook APIs remain only for backward compatibility and will be removed in a future release. + +### Migrating from lifecycle hooks + +If you're migrating from the deprecated `IDistributedApplicationLifecycleHook` interface, use the following mapping: + +| Old pattern (deprecated) | New pattern | +|--------------------------|-------------| +| `BeforeStartAsync()` | Subscribe to `BeforeStartEvent` | +| `AfterEndpointsAllocatedAsync()` | Subscribe to `ResourceEndpointsAllocatedEvent` | +| `AfterResourcesCreatedAsync()` | Subscribe to `AfterResourcesCreatedEvent` | +| `TryAddLifecycleHook()` | `TryAddEventingSubscriber()` | + +**Before (deprecated):** + +```csharp title="OldLifecycleHook.cs" +public class MyHook : IDistributedApplicationLifecycleHook +{ + public Task AfterResourcesCreatedAsync( + DistributedApplicationModel model, + CancellationToken cancellationToken) + { + // Handle event + return Task.CompletedTask; + } +} + +// Registration +builder.Services.TryAddLifecycleHook(); +``` + +**After (recommended):** + +```csharp title="NewEventingSubscriber.cs" +public class MySubscriber : IDistributedApplicationEventingSubscriber +{ + public Task SubscribeAsync( + IDistributedApplicationEventing eventing, + DistributedApplicationExecutionContext context, + CancellationToken cancellationToken) + { + eventing.Subscribe((@event, ct) => + { + // Handle event using context.Model + return Task.CompletedTask; + }); + + return Task.CompletedTask; + } +} + +// Registration +builder.Services.TryAddEventingSubscriber(); +``` + +

+ +## Additional events + +Beyond the core lifecycle events, Aspire provides additional events for specific scenarios: + +### Publishing events + +When publishing your application (generating deployment manifests), these events are raised: + +| Event | When raised | Purpose | +|-------|-------------|---------| +| `BeforePublishEvent` | Before publishing begins | Validate or modify resources before manifest generation | +| `AfterPublishEvent` | After publishing completes | Perform cleanup or post-publish actions | + +```csharp title="AppHost.cs" +builder.Eventing.Subscribe((@event, ct) => +{ + // Validate resources before publishing + return Task.CompletedTask; +}); + +builder.Eventing.Subscribe((@event, ct) => +{ + // Post-publish actions + return Task.CompletedTask; +}); +``` + + +For details on what happens during publishing, see [Publishing and deployment overview](/deployment/overview/). + + +### Resource stopped event + +The `ResourceStoppedEvent` is raised when a resource stops execution: + +```csharp title="AppHost.cs" +builder.Eventing.Subscribe( + cache, + (@event, ct) => + { + logger.LogInformation("Resource {Name} stopped", @event.Resource.Name); + return Task.CompletedTask; + }); +``` + + + +## See also + +- [Custom resources](/extensibility/custom-resources/) +- [Resource annotations](/fundamentals/annotations-overview/) diff --git a/src/frontend/src/content/docs/architecture/glossary.md b/src/frontend/src/content/docs/architecture/glossary.md deleted file mode 100644 index a1a8acfb..00000000 --- a/src/frontend/src/content/docs/architecture/glossary.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Glossary -description: A glossary of key terms and concepts in Aspire. -giscus: false -next: false -prev: false ---- - -| API Terms | Description | -| --------------------- | ------------------------------------------------- | -| `IResourceAnnotation` | Typed metadata object attached to resources. | -| `WithAnnotation()` | Fluent method to attach typed annotations. | -| `ReferenceExpression` | Structured formatter preserving value references. | - -| Term | Definition | -| ----------------------------- | -------------------------------------------------- | -| Resource | Service or infrastructure element in your app. | -| Annotation | Metadata attached to a resource. | -| DAG | Directed acyclic graph. | -| Heterogeneous DAG | DAG containing varied resource types. | -| Publisher | Emits deployment artifacts from the model. | -| Hoisting | Leaving a value unresolved for later substitution. | -| Deferred evaluation | Computing a value only when needed. | -| `ResourceNotificationService` | Publishes observable state updates. | -| Lifecycle events | Time-based signals for resource transitions. | diff --git a/src/frontend/src/content/docs/architecture/resource-examples.mdx b/src/frontend/src/content/docs/architecture/resource-examples.mdx index 01180ef3..d2318d70 100644 --- a/src/frontend/src/content/docs/architecture/resource-examples.mdx +++ b/src/frontend/src/content/docs/architecture/resource-examples.mdx @@ -213,87 +213,89 @@ public class RedisResource(string name) This example demonstrates creating a completely custom resource (`TalkingClockResource`) that doesn't derive from built-in types. It shows: - Defining a simple resource class. -- Implementing a custom lifecycle hook (`TalkingClockLifecycleHook`) to manage the resource's behavior (starting, logging, state updates). +- Implementing a custom eventing subscriber (`TalkingClockEventingSubscriber`) to manage the resource's behavior (starting, logging, state updates). - Using `ResourceLoggerService` for per-resource logging. - Using `ResourceNotificationService` to publish state updates. -- Creating an `AddTalkingClock` extension method to register the resource and its lifecycle hook. +- Creating an `AddTalkingClock` extension method to register the resource and its eventing subscriber. ```csharp title="C# — TalkingClockResource.cs" // Define the custom resource type. It inherits from the base Aspire 'Resource' class. -// This class is primarily a data container; Aspire behavior is added via lifecycle hooks and extension methods. +// This class is primarily a data container; Aspire behavior is added via eventing subscribers and extension methods. public sealed class TalkingClockResource(string name) : Resource(name); ``` -```csharp title="C# — TalkingClockLifecycleHook.cs" -// Define an Aspire lifecycle hook that implements the behavior for the TalkingClockResource. -// Lifecycle hooks allow plugging into the application's startup and shutdown sequences. -public sealed class TalkingClockLifecycleHook( +```csharp title="C# — TalkingClockEventingSubscriber.cs" +// Define an Aspire eventing subscriber that implements the behavior for the TalkingClockResource. +// Eventing subscribers allow plugging into the application's startup and lifecycle events. +public sealed class TalkingClockEventingSubscriber( // Aspire service for publishing resource state updates (e.g., Running, Starting). ResourceNotificationService notification, - // Aspire service for publishing and subscribing to application-wide events. - IDistributedApplicationEventing eventing, // Aspire service for getting a logger scoped to a specific resource. ResourceLoggerService loggerSvc, // General service provider for dependency injection if needed. - IServiceProvider services) : IDistributedApplicationLifecycleHook // Implement the Aspire hook interface. + IServiceProvider services) : IDistributedApplicationEventingSubscriber // Implement the Aspire eventing subscriber interface. { - // This method is called by Aspire after all resources have been initially added to the application model. - public Task AfterResourcesCreatedAsync( - DistributedApplicationModel model, // The Aspire application model containing all resources. - CancellationToken token) // Cancellation token for graceful shutdown. + // This method is called by Aspire to allow subscription to lifecycle events. + public Task SubscribeAsync( + IDistributedApplicationEventing eventing, // The eventing service to subscribe to. + DistributedApplicationExecutionContext context, // Execution context with model and environment info. + CancellationToken cancellationToken) // Cancellation token for graceful shutdown. { - // Find all instances of TalkingClockResource in the Aspire application model. - foreach (var clock in model.Resources.OfType()) + // Subscribe to the AfterResourcesCreatedEvent to start the clock behavior. + eventing.Subscribe(async (@event, ct) => { - // Get an Aspire logger specifically for this clock instance. Logs will be associated with this resource in the dashboard. - var log = loggerSvc.GetLogger(clock); - - // Start a background task to manage the clock's lifecycle and behavior. - _ = Task.Run(async () => + // Find all instances of TalkingClockResource in the Aspire application model. + foreach (var clock in context.Model.Resources.OfType()) { - // Publish an Aspire event indicating that this resource is about to start. - // Other components could subscribe to this event for pre-start actions. - await eventing.PublishAsync( - new BeforeResourceStartedEvent(clock, services), token); - - // Log an informational message associated with the resource. - log.LogInformation("Starting Talking Clock..."); - - // Publish an initial state update to the Aspire notification service. - // This sets the resource's state to 'Running' and records the start time. - // The Aspire dashboard and other orchestrators observe these state updates. - await notification.PublishUpdateAsync(clock, s => s with - { - StartTimeStamp = DateTime.UtcNow, - State = KnownResourceStates.Running // Use an Aspire well-known state. - }); + // Get an Aspire logger specifically for this clock instance. + // Logs will be associated with this resource in the dashboard. + var log = loggerSvc.GetLogger(clock); - // Enter the main loop that runs as long as cancellation is not requested. - while (!token.IsCancellationRequested) + // Start a background task to manage the clock's lifecycle and behavior. + _ = Task.Run(async () => { - // Log the current time, associated with the resource. - log.LogInformation("The time is {time}", DateTime.UtcNow); + // Publish an Aspire event indicating that this resource is about to start. + // Other components could subscribe to this event for pre-start actions. + await eventing.PublishAsync( + new BeforeResourceStartedEvent(clock, services), ct); + + // Log an informational message associated with the resource. + log.LogInformation("Starting Talking Clock..."); + + // Publish an initial state update to the Aspire notification service. + // This sets the resource's state to 'Running' and records the start time. + // The Aspire dashboard and other orchestrators observe these state updates. + await notification.PublishUpdateAsync(clock, s => s with + { + StartTimeStamp = DateTime.UtcNow, + State = KnownResourceStates.Running // Use an Aspire well-known state. + }); - // Publish a custom state update "Tick" using Aspire's ResourceStateSnapshot. - // This demonstrates using custom state strings and styles in the Aspire dashboard. - await notification.PublishUpdateAsync(clock, - s => s with { State = new ResourceStateSnapshot("Tick", KnownResourceStateStyles.Info) }); + // Enter the main loop that runs as long as cancellation is not requested. + while (!ct.IsCancellationRequested) + { + // Log the current time, associated with the resource. + log.LogInformation("The time is {time}", DateTime.UtcNow); - await Task.Delay(1000, token); + // Publish a custom state update "Tick" using Aspire's ResourceStateSnapshot. + // This demonstrates using custom state strings and styles in the Aspire dashboard. + await notification.PublishUpdateAsync(clock, + s => s with { State = new ResourceStateSnapshot("Tick", KnownResourceStateStyles.Info) }); - // Publish another custom state update "Tock" using Aspire's ResourceStateSnapshot. - await notification.PublishUpdateAsync(clock, - s => s with { State = new ResourceStateSnapshot("Tock", KnownResourceStateStyles.Success) }); + await Task.Delay(1000, ct); - await Task.Delay(1000, token); - } - }, token); - } + // Publish another custom state update "Tock" using Aspire's ResourceStateSnapshot. + await notification.PublishUpdateAsync(clock, + s => s with { State = new ResourceStateSnapshot("Tock", KnownResourceStateStyles.Success) }); + + await Task.Delay(1000, ct); + } + }, ct); + } + }); - // Indicate that this hook's work (starting the background tasks) is complete for now. return Task.CompletedTask; } - // Other Aspire lifecycle hook methods (e.g., BeforeStartAsync, AfterEndpointsAllocatedAsync) could be implemented here if needed. } ``` @@ -308,9 +310,9 @@ public static class TalkingClockExtensions this IDistributedApplicationBuilder builder, // Extends the Aspire application builder. string name) // The name for this resource instance. { - // Register the TalkingClockLifecycleHook with the DI container using Aspire's helper method. - // The Aspire hosting infrastructure will automatically discover and run registered lifecycle hooks. - builder.Services.TryAddLifecycleHook(); + // Register the TalkingClockEventingSubscriber with the DI container using Aspire's helper method. + // The Aspire hosting infrastructure will automatically discover and run registered eventing subscribers. + builder.Services.TryAddEventingSubscriber(); // Create a new instance of the TalkingClockResource. var clockResource = new TalkingClockResource(name); diff --git a/src/frontend/src/content/docs/diagnostics/aspirecontainershellexecution001.mdx b/src/frontend/src/content/docs/diagnostics/aspirecontainershellexecution001.mdx new file mode 100644 index 00000000..d9cb83e1 --- /dev/null +++ b/src/frontend/src/content/docs/diagnostics/aspirecontainershellexecution001.mdx @@ -0,0 +1,46 @@ +--- +title: ASPIRECONTAINERSHELLEXECUTION001 +description: The container shell execution property is experimental and subject to change. +--- + +## Overview + +| Property | Value | +|----------|-------| +| **Diagnostic ID** | ASPIRECONTAINERSHELLEXECUTION001 | +| **Severity** | Warning | +| **Category** | Usage | + +The `ShellExecution` property on `ContainerResource` is an experimental feature that controls whether custom arguments should be wrapped for shell execution. This API may change or be removed in future versions. + +## Cause + +This warning appears when you set the `ShellExecution` property on a container resource: + +```csharp +container.ShellExecution = true; +``` + +When enabled, custom arguments are wrapped in `-c "values"` format for shell execution. + +## How to suppress + +If you understand the experimental nature of this API and want to use it, suppress the warning: + +```csharp title="Suppress in code" +#pragma warning disable ASPIRECONTAINERSHELLEXECUTION001 +container.ShellExecution = true; +#pragma warning restore ASPIRECONTAINERSHELLEXECUTION001 +``` + +Or in your project file: + +```xml title="Suppress in project file" + + $(NoWarn);ASPIRECONTAINERSHELLEXECUTION001 + +``` + +## See also + +- [Container resources](/get-started/app-host/#add-container-resource) - Learn about container resources diff --git a/src/frontend/src/content/docs/diagnostics/aspiredotnettool.mdx b/src/frontend/src/content/docs/diagnostics/aspiredotnettool.mdx new file mode 100644 index 00000000..fddcf819 --- /dev/null +++ b/src/frontend/src/content/docs/diagnostics/aspiredotnettool.mdx @@ -0,0 +1,44 @@ +--- +title: ASPIREDOTNETTOOL +description: The .NET tool resource APIs are experimental and subject to change. +--- + +## Overview + +| Property | Value | +|----------|-------| +| **Diagnostic ID** | ASPIREDOTNETTOOL | +| **Severity** | Warning | +| **Category** | Usage | + +The `DotnetToolResource` and related APIs for adding .NET CLI tools as resources are experimental features. These APIs may change or be removed in future versions. + +## Cause + +This warning appears when you use APIs for adding .NET tools as resources: + +- `DotnetToolResource` +- `DotnetToolAnnotation` +- `AddDotnetTool` extension methods + +## How to suppress + +If you understand the experimental nature of these APIs and want to use them, suppress the warning: + +```csharp title="Suppress in code" +#pragma warning disable ASPIREDOTNETTOOL +var tool = builder.AddDotnetTool("my-tool", "dotnet-tool-package"); +#pragma warning restore ASPIREDOTNETTOOL +``` + +Or in your project file: + +```xml title="Suppress in project file" + + $(NoWarn);ASPIREDOTNETTOOL + +``` + +## See also + +- [Executables](/get-started/app-host/#add-executable-resource) - Learn about executable resources diff --git a/src/frontend/src/content/docs/diagnostics/aspireextension001.mdx b/src/frontend/src/content/docs/diagnostics/aspireextension001.mdx new file mode 100644 index 00000000..2cb93830 --- /dev/null +++ b/src/frontend/src/content/docs/diagnostics/aspireextension001.mdx @@ -0,0 +1,49 @@ +--- +title: ASPIREEXTENSION001 +description: The extension debugging support APIs are experimental and subject to change. +--- + +## Overview + +| Property | Value | +|----------|-------| +| **Diagnostic ID** | ASPIREEXTENSION001 | +| **Severity** | Warning | +| **Category** | Usage | + +The `WithDebugSupport` extension method and related APIs for enabling debugging support in IDE extensions are experimental features. These APIs may change or be removed in future versions. + +## Cause + +This warning appears when you use APIs for adding debug support to resources: + +- `WithDebugSupport` extension method +- `SupportsDebuggingAnnotation` + +These APIs are primarily used by integration authors to enable F5 debugging for custom resource types in Visual Studio and other IDEs. + +## How to suppress + +If you understand the experimental nature of these APIs and want to use them, suppress the warning: + +```csharp title="Suppress in code" +#pragma warning disable ASPIREEXTENSION001 +builder.AddMyResource("resource") + .WithDebugSupport( + processId => new MyLaunchConfiguration { ProcessId = processId }, + "myResourceType"); +#pragma warning restore ASPIREEXTENSION001 +``` + +Or in your project file: + +```xml title="Suppress in project file" + + $(NoWarn);ASPIREEXTENSION001 + +``` + +## See also + +- [Debugging overview](/fundamentals/debugging/) - Learn about debugging Aspire applications +- [Custom hosting integrations](/integrations/custom-integrations/hosting-integrations/) - Learn about creating custom integrations diff --git a/src/frontend/src/content/docs/diagnostics/aspireinteraction001.mdx b/src/frontend/src/content/docs/diagnostics/aspireinteraction001.mdx index 2adf7b5e..b959377d 100644 --- a/src/frontend/src/content/docs/diagnostics/aspireinteraction001.mdx +++ b/src/frontend/src/content/docs/diagnostics/aspireinteraction001.mdx @@ -67,6 +67,42 @@ dotnet_diagnostic.ASPIREINTERACTION001.severity = none ### Suppressing in a project file ```xml title="YourProject.csproj" +title: ASPIREINTERACTION001 +description: The interaction service APIs are experimental and subject to change. +--- + +## Overview + +| Property | Value | +|----------|-------| +| **Diagnostic ID** | ASPIREINTERACTION001 | +| **Severity** | Warning | +| **Category** | Usage | + +The `IInteractionService` and related APIs are experimental features that enable interactive prompts and confirmations during AppHost execution. These APIs may change or be removed in future versions. + +## Cause + +This warning appears when you use the interaction service APIs such as: + +- `IInteractionService.PromptAsync` +- `IInteractionService.ConfirmAsync` +- `IInteractionService.SelectAsync` +- `InputGeneratorAnnotation` + +## How to suppress + +If you understand the experimental nature of these APIs and want to use them, suppress the warning: + +```csharp title="Suppress in code" +#pragma warning disable ASPIREINTERACTION001 +var result = await interactionService.PromptAsync("Enter value:", cancellationToken); +#pragma warning restore ASPIREINTERACTION001 +``` + +Or in your project file: + +```xml title="Suppress in project file" $(NoWarn);ASPIREINTERACTION001 @@ -85,4 +121,4 @@ var result = await interactionService.PromptConfirmationAsync( ## See also -- [Interaction service (Preview)](/extensibility/interaction-service/) +- [Interaction service](/extensibility/interaction-service/) - Learn how to use the interaction service diff --git a/src/frontend/src/content/docs/diagnostics/aspirepostgres001.mdx b/src/frontend/src/content/docs/diagnostics/aspirepostgres001.mdx new file mode 100644 index 00000000..1d9e7fef --- /dev/null +++ b/src/frontend/src/content/docs/diagnostics/aspirepostgres001.mdx @@ -0,0 +1,43 @@ +--- +title: ASPIREPOSTGRES001 +description: The PostgreSQL MCP integration APIs are experimental and subject to change. +--- + +## Overview + +| Property | Value | +|----------|-------| +| **Diagnostic ID** | ASPIREPOSTGRES001 | +| **Severity** | Warning | +| **Category** | Usage | + +The `WithPostgresMcp` extension method for adding Model Context Protocol (MCP) support to PostgreSQL resources is an experimental feature. This API may change or be removed in future versions. + +## Cause + +This warning appears when you use the `WithPostgresMcp` extension method to add MCP capabilities to a PostgreSQL database resource. + +## How to suppress + +If you understand the experimental nature of this API and want to use it, suppress the warning: + +```csharp title="Suppress in code" +#pragma warning disable ASPIREPOSTGRES001 +var db = builder.AddAzurePostgresFlexibleServer("postgres") + .AddDatabase("mydb") + .WithPostgresMcp(); +#pragma warning restore ASPIREPOSTGRES001 +``` + +Or in your project file: + +```xml title="Suppress in project file" + + $(NoWarn);ASPIREPOSTGRES001 + +``` + +## See also + +- [PostgreSQL integration](/integrations/databases/postgresql/postgresql-host/) - Learn about PostgreSQL hosting integration +- [Configure MCP](/get-started/configure-mcp/) - Learn about Model Context Protocol configuration diff --git a/src/frontend/src/content/docs/diagnostics/overview.mdx b/src/frontend/src/content/docs/diagnostics/overview.mdx index 01c232b8..834421f7 100644 --- a/src/frontend/src/content/docs/diagnostics/overview.mdx +++ b/src/frontend/src/content/docs/diagnostics/overview.mdx @@ -24,20 +24,25 @@ The following table lists the possible MSBuild and analyzer warnings and errors | [ASPIRECOMPUTE002](/diagnostics/aspirecompute002/) | (Experimental) Warning | The GetHostAddressExpression method is for evaluation purposes only and is subject to change or removal in future updates. | | [ASPIRECOMPUTE003](/diagnostics/aspirecompute003/) | (Experimental) Warning | The ContainerRegistryResource type is for evaluation purposes only and is subject to change or removal in future updates. | | [ASPIRECONTAINERRUNTIME001](/diagnostics/aspirecontainerruntime001/) | (Experimental) Warning | Type is for evaluation purposes only and is subject to change or removal in future updates. | -| [ASPIRECSHARPAPPS001](/diagnostics/aspirecsharpapps001/) | (Experimental) Error | `AddCSharpApp` is for evaluation purposes only and is subject to change or removal in future updates. | +| [ASPIRECONTAINERSHELLEXECUTION001](/diagnostics/aspirecontainershellexecution001/) | (Experimental) Warning | Container shell execution property is for evaluation purposes only and is subject to change or removal in future updates. | | [ASPIRECOSMOSDB001](/diagnostics/aspirecosmosdb001/) | (Experimental) Error | `RunAsPreviewEmulator` is for evaluation purposes only and is subject to change or removal in future updates. | +| [ASPIRECSHARPAPPS001](/diagnostics/aspirecsharpapps001/) | (Experimental) Error | `AddCSharpApp` is for evaluation purposes only and is subject to change or removal in future updates. | | [ASPIREDOCKERFILEBUILDER001](/diagnostics/aspiredockerfilebuilder001/) | (Experimental) Warning | Dockerfile builder types and members are for evaluation purposes only and are subject to change or removal in future updates. | +| [ASPIREDOTNETTOOL](/diagnostics/aspiredotnettool/) | (Experimental) Warning | .NET tool resource types and members are for evaluation purposes only and are subject to change or removal in future updates. | +| [ASPIREEXTENSION001](/diagnostics/aspireextension001/) | (Experimental) Warning | Extension debugging support APIs are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREFILESYSTEM001](/diagnostics/aspirefilesystem001/) | (Experimental) Warning | File system service types and members are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREINTERACTION001](/diagnostics/aspireinteraction001/) | (Experimental) Warning | Interaction service types and members are for evaluation purposes only and are subject to change or removal in future updates. | -| [ASPIREPIPELINES004](/diagnostics/aspirepipelines004/) | (Experimental) Warning | Type is for evaluation purposes only and is subject to change or removal in future updates. | -| [ASPIREPROBES001](/diagnostics/aspireprobes001/) | (Experimental) Warning | Probe-related types and members are for evaluation purposes only and are subject to change or removal in future updates. | -| [ASPIREUSERSECRETS001](/diagnostics/aspireusersecrets001/) | (Experimental) Warning | Type is for evaluation purposes only and is subject to change or removal in future updates. | | [ASPIREHOSTINGPYTHON001](/diagnostics/aspirehostingpython001/) | (Experimental) Error | `AddPythonApp` is for evaluation purposes only and is subject to change or removal in future updates. | +| [ASPIREINTERACTION001](/diagnostics/aspireinteraction001/) | (Experimental) Warning | Interaction service APIs are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREPIPELINES001](/diagnostics/aspirepipelines001/) | (Experimental) Error | Pipeline infrastructure APIs are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREPIPELINES002](/diagnostics/aspirepipelines002/) | (Experimental) Error | Deployment state manager APIs are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREPIPELINES003](/diagnostics/aspirepipelines003/) | (Experimental) Error | Container image build APIs are for evaluation purposes only and are subject to change or removal in future updates. | +| [ASPIREPIPELINES004](/diagnostics/aspirepipelines004/) | (Experimental) Warning | Type is for evaluation purposes only and is subject to change or removal in future updates. | +| [ASPIREPOSTGRES001](/diagnostics/aspirepostgres001/) | (Experimental) Warning | PostgreSQL MCP integration is for evaluation purposes only and is subject to change or removal in future updates. | +| [ASPIREPROBES001](/diagnostics/aspireprobes001/) | (Experimental) Warning | Probe-related types and members are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREPROXYENDPOINTS001](/diagnostics/aspireproxyendpoints001/) | (Experimental) Error | ProxyEndpoint members are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREPUBLISHERS001](/diagnostics/aspirepublishers001/) | Error | Publishers are for evaluation purposes only and are subject to change or removal in future updates. | +| [ASPIREUSERSECRETS001](/diagnostics/aspireusersecrets001/) | (Experimental) Warning | Type is for evaluation purposes only and is subject to change or removal in future updates. | ## Suppress diagnostic diff --git a/src/frontend/src/content/docs/extensibility/custom-resources.mdx b/src/frontend/src/content/docs/extensibility/custom-resources.mdx new file mode 100644 index 00000000..bf83958e --- /dev/null +++ b/src/frontend/src/content/docs/extensibility/custom-resources.mdx @@ -0,0 +1,292 @@ +--- +title: Aspire custom resources +description: Learn how to create custom resource types for the .NET Aspire app host to model your application's unique infrastructure components. +--- + +import { Aside } from '@astrojs/starlight/components'; +import LearnMore from '@components/LearnMore.astro'; + +Creating custom resources allows you to extend Aspire to model components that aren't covered by built-in integrations. This guide covers the patterns and APIs for building your own resource types. + +## Resource fundamentals + +All Aspire resources implement the `IResource` interface, which requires a `Name` property. The base `Resource` class provides a simple implementation you can inherit from. + +### Basic resource definition + +The simplest custom resource is a class that inherits from `Resource`: + +```csharp title="MailDevResource.cs" +public sealed class MailDevResource(string name) : Resource(name); +``` + +This creates a resource that can be added to the app model but doesn't have any special behavior. You'll typically want to implement additional interfaces to add capabilities. + +## Common resource interfaces + +Aspire provides several interfaces that give resources specific capabilities: + +| Interface | Purpose | +|-----------|---------| +| `IResourceWithConnectionString` | Resource exposes a connection string for consumers | +| `IResourceWithEndpoints` | Resource has network endpoints | +| `IResourceWithEnvironment` | Resource can configure environment variables | +| `IResourceWithArgs` | Resource accepts command-line arguments | +| `IResourceWithWaitSupport` | Resource supports `WaitFor` orchestration | +| `IResourceWithParent` | Resource has a parent resource (lifecycle binding) | + +### Implementing IResourceWithConnectionString + +Resources that expose connection strings for client applications should implement `IResourceWithConnectionString`: + +```csharp title="MailDevResource.cs" +public sealed class MailDevResource(string name, EndpointReference smtpEndpoint) + : Resource(name), IResourceWithConnectionString +{ + public ReferenceExpression ConnectionStringExpression => + ReferenceExpression.Create( + $"smtp://{smtpEndpoint.Property(EndpointProperty.Host)}:{smtpEndpoint.Property(EndpointProperty.Port)}"); +} +``` + +For resources with authentication, include credentials in the connection string: + +```csharp title="InfluxDbResource.cs" +public sealed class InfluxDbResource : Resource, IResourceWithConnectionString +{ + private readonly EndpointReference _endpoint; + private readonly ParameterResource _token; + + public InfluxDbResource( + string name, + EndpointReference endpoint, + ParameterResource token) : base(name) + { + _endpoint = endpoint; + _token = token; + } + + public ReferenceExpression ConnectionStringExpression => + ReferenceExpression.Create( + $"Endpoint={_endpoint.Property(EndpointProperty.UriString)};" + + $"Token={_token}"); +} +``` + +## Creating extension methods + +By convention, resources are added to the app model via extension methods on `IDistributedApplicationBuilder`: + +```csharp title="MailDevExtensions.cs" +public static class MailDevExtensions +{ + public static IResourceBuilder AddMailDev( + this IDistributedApplicationBuilder builder, + [ResourceName] string name, + int? smtpPort = null) + { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + + var resource = new MailDevResource(name); + + return builder.AddResource(resource) + .WithImage("maildev/maildev", "2.1.0") + .WithHttpEndpoint(targetPort: 1080, name: "http") + .WithEndpoint(targetPort: 1025, port: smtpPort, name: "smtp"); + } +} +``` + +The `[ResourceName]` attribute enables IDE tooling and validation for resource names. + +## Adding resource behavior + +Resources are data containers by default. To add runtime behavior, use one of these approaches: + +### Using eventing subscribers + +The recommended approach for adding lifecycle behavior is implementing `IDistributedApplicationEventingSubscriber`: + +```csharp title="MailDevEventingSubscriber.cs" +public sealed class MailDevEventingSubscriber( + ResourceNotificationService notification, + ResourceLoggerService loggerService) + : IDistributedApplicationEventingSubscriber +{ + public Task SubscribeAsync( + IDistributedApplicationEventing eventing, + DistributedApplicationExecutionContext context, + CancellationToken cancellationToken) + { + eventing.Subscribe(async (@event, ct) => + { + foreach (var resource in context.Model.Resources.OfType()) + { + var logger = loggerService.GetLogger(resource); + logger.LogInformation("MailDev server ready at {Name}", resource.Name); + + await notification.PublishUpdateAsync(resource, s => s with + { + State = KnownResourceStates.Running, + StartTimeStamp = DateTime.UtcNow + }); + } + }); + + return Task.CompletedTask; + } +} +``` + + +For the full list of available events and advanced patterns, see [AppHost eventing](/app-host/eventing/). + + +Register the subscriber in your extension method: + +```csharp title="MailDevExtensions.cs" +public static IResourceBuilder AddMailDev( + this IDistributedApplicationBuilder builder, + [ResourceName] string name, + int? smtpPort = null) +{ + builder.Services.TryAddEventingSubscriber(); + + var resource = new MailDevResource(name); + return builder.AddResource(resource); +} +``` + +### Using inline event subscriptions + +For simpler cases, you can subscribe to events directly on the resource builder: + +```csharp title="AppHost.cs" +public static IResourceBuilder AddMailDev( + this IDistributedApplicationBuilder builder, + [ResourceName] string name) +{ + var resource = new MailDevResource(name); + + builder.Eventing.Subscribe(resource, async (@event, ct) => + { + // Initialize mail server, create test mailboxes, etc. + }); + + return builder.AddResource(resource); +} +``` + +## Resource state management + +Use `ResourceNotificationService` to publish state updates that appear in the dashboard: + +```csharp title="Publishing state updates" +await notification.PublishUpdateAsync(resource, state => state with +{ + State = KnownResourceStates.Running, + StartTimeStamp = DateTime.UtcNow +}); +``` + +### Well-known states + +Aspire provides several well-known states in `KnownResourceStates`: + +- `NotStarted` - Resource hasn't started yet +- `Starting` - Resource is initializing +- `Running` - Resource is operational +- `Stopping` - Resource is shutting down +- `Exited` - Resource has stopped +- `FailedToStart` - Resource failed during startup +- `Waiting` - Resource is waiting for dependencies +- `Hidden` - Resource is hidden from the dashboard + +### Custom states + +You can create custom states using `ResourceStateSnapshot`: + +```csharp title="Custom state example" +await notification.PublishUpdateAsync(resource, state => state with +{ + State = new ResourceStateSnapshot("Indexing", KnownResourceStateStyles.Info) +}); +``` + +## Initial state configuration + +Set the initial dashboard appearance using `WithInitialState`: + +```csharp title="Setting initial state" +return builder.AddResource(resource) + .WithInitialState(new CustomResourceSnapshot + { + ResourceType = "MailDev", + CreationTimeStamp = DateTime.UtcNow, + State = KnownResourceStates.NotStarted, + Properties = [ + new(CustomResourceKnownProperties.Source, "MailDev SMTP Server") + ] + }); +``` + +## Excluding from manifest + +If your resource is for local development only, exclude it from deployment manifests: + +```csharp title="Excluding from deployment" +return builder.AddResource(resource) + .ExcludeFromManifest(); +``` + +## Resource relationships + +Use relationships to organize how resources appear in the dashboard: + +### WithRelationship + +Create custom relationships between resources for visual organization: + +```csharp title="AppHost.cs" +var api = builder.AddProject("api"); +var worker = builder.AddProject("worker") + .WithRelationship(api.Resource, "publishes-to"); +``` + +### WithChildRelationship + +Group related resources under a parent in the dashboard: + +```csharp title="AppHost.cs" +var postgres = builder.AddPostgres("postgres"); +var catalogDb = postgres.AddDatabase("catalog"); + +// Custom resources can establish parent-child relationships: +var mailDev = builder.AddMailDev("mail") + .WithChildRelationship(catalogDb); +``` + + +For more on how the Aspire Dashboard displays resources, see [Dashboard overview](/dashboard/overview/). + + +## Custom icons + +Use `WithIconName` to display a custom icon for your resource in the dashboard. Any [Fluent UI system icon](https://github.com/microsoft/fluentui-system-icons/blob/main/icons_filled.md) can be used: + +```csharp title="Setting custom dashboard icons" +return builder.AddResource(resource) + .WithIconName("mail"); // Uses the "mail" Fluent UI icon + +// Or specify a variant (Filled is default, Regular is outline-only) +return builder.AddResource(resource) + .WithIconName("mail", IconVariant.Regular); +``` + +## See also + +- [AppHost eventing](/app-host/eventing/) - Lifecycle events and subscribers +- [Resource annotations](/fundamentals/annotations-overview/) - Creating and using annotations +- [Resource examples](/architecture/resource-examples/) - Real-world custom resource examples +- [Custom resource commands](/fundamentals/custom-resource-commands/) - Adding dashboard commands diff --git a/src/frontend/src/content/docs/fundamentals/annotations-overview.mdx b/src/frontend/src/content/docs/fundamentals/annotations-overview.mdx index 53628ce9..6675c88b 100644 --- a/src/frontend/src/content/docs/fundamentals/annotations-overview.mdx +++ b/src/frontend/src/content/docs/fundamentals/annotations-overview.mdx @@ -4,6 +4,7 @@ description: Learn about annotations in Aspire, how they work, and how to create --- import { Aside } from '@astrojs/starlight/components'; +import LearnMore from '@components/LearnMore.astro'; Annotations are a key extensibility mechanism in Aspire that allow you to attach metadata and behavior to resources. They provide a way to customize how resources are configured, deployed, and managed throughout the application lifecycle. This article explains how annotations work and how to use them effectively in your Aspire applications. @@ -40,6 +41,38 @@ In this example: - `WithCommand` adds a `ResourceCommandAnnotation` that defines a custom command. - `WithUrl` adds a `ResourceUrlAnnotation` that defines a custom URL. +## Adding and managing annotations + +The `WithAnnotation` extension method is the primary way to add annotations to resources. It supports different behaviors when an annotation of the same type already exists: + +```csharp title="AppHost.cs" +// Add only if no annotation of this type exists (default) +builder.AddContainer("mycontainer", "myimage") + .WithAnnotation(new MyAnnotation(), ResourceAnnotationMutationBehavior.None); + +// Replace any existing annotation of this type +builder.AddContainer("mycontainer", "myimage") + .WithAnnotation(new MyAnnotation(), ResourceAnnotationMutationBehavior.Replace); +``` + +### Reading annotations + +To read annotations from a resource, use the `Annotations` collection or helper methods: + +```csharp title="Reading annotations" +// Get all annotations of a type +var endpoints = resource.Annotations.OfType(); + +// Try to get the last annotation of a type (most recently added) +if (resource.TryGetLastAnnotation(out var endpoint)) +{ + Console.WriteLine($"Endpoint: {endpoint.Name}"); +} + +// Check if an annotation exists +bool hasEndpoints = resource.Annotations.OfType().Any(); +``` + ## Built-in annotation types Aspire includes many built-in annotation types for common scenarios. This section covers _some_ of the more commonly used annotations, but there are many more available for specific use cases. @@ -120,6 +153,70 @@ api.Resource.Annotations.Add( For more information about publishing and deploying Aspire apps, see [publishing and deploying](/deployment/overview/). +### `HealthCheckAnnotation` + +The `HealthCheckAnnotation` associates health checks with a resource, used by `WaitFor()` to determine when a resource is ready: + +```csharp title="AppHost.cs" +var api = builder.AddProject("api") + .WithHttpHealthCheck("/health"); // Adds HealthCheckAnnotation +``` + + +Learn more about how Aspire uses health checks for orchestration in the [Health checks guide](/fundamentals/health-checks/). + + +### `WaitAnnotation` + +The `WaitAnnotation` declares a dependency on another resource and controls startup order. The `WaitFor`, `WaitForStart`, and `WaitForCompletion` methods add this annotation: + +```csharp title="AppHost.cs" +var cache = builder.AddRedis("cache"); + +// Wait for cache to be healthy before starting web +var web = builder.AddProject("web") + .WaitFor(cache); // Adds WaitAnnotation with WaitType.WaitForHealthy +``` + +The `WaitType` enum specifies what condition to wait for: + +| Wait type | Description | +|----------|-------------| +| `WaitForStart` | Wait for resource to start (not necessarily healthy) | +| `WaitForHealthy` | Wait for resource to be healthy (default for `WaitFor`) | +| `WaitForCompletion` | Wait for resource to exit (for one-time tasks) | + +### `ReplicaAnnotation` + +The `ReplicaAnnotation` specifies how many instances of a resource should run: + +```csharp title="AppHost.cs" +var api = builder.AddProject("api") + .WithReplicas(3); // Adds ReplicaAnnotation +``` + +### `ContainerLifetimeAnnotation` + +The `ContainerLifetimeAnnotation` controls whether a container persists across app runs: + +```csharp title="AppHost.cs" +var postgres = builder.AddPostgres("postgres") + .WithLifetime(ContainerLifetime.Persistent); // Container survives restarts +``` + + +For details on persistent containers and their use cases, see [Persistent containers](/app-host/persistent-containers/). + + +### `ExplicitStartupAnnotation` + +The `ExplicitStartupAnnotation` prevents a resource from starting automatically. Users must manually start it from the dashboard: + +```csharp title="AppHost.cs" +var worker = builder.AddProject("batch") + .WithExplicitStart(); // Adds ExplicitStartupAnnotation +``` + ## Creating custom annotations Custom annotations in Aspire are designed to capture resource-specific metadata and behavior that can be leveraged throughout the application lifecycle. They're commonly used by: @@ -294,3 +391,9 @@ public sealed class ConfigAnnotation : IResourceAnnotation public SecuritySettings Security { get; set; } } ``` + +## See also + +- [Custom resources](/extensibility/custom-resources/) - Creating custom resource types that use annotations +- [Resource API patterns](/architecture/resource-api-patterns/) - Advanced API patterns for resources +- [AppHost eventing](/app-host/eventing/) - Handling lifecycle events in resources diff --git a/src/frontend/src/content/docs/fundamentals/health-checks.mdx b/src/frontend/src/content/docs/fundamentals/health-checks.mdx index 2f33fb46..574362a7 100644 --- a/src/frontend/src/content/docs/fundamentals/health-checks.mdx +++ b/src/frontend/src/content/docs/fundamentals/health-checks.mdx @@ -3,21 +3,33 @@ title: Health checks description: Explore Aspire health checks --- +import { Aside } from '@astrojs/starlight/components'; import { Image } from 'astro:assets'; import healthChecksDashboardStatus from '@assets/fundamentals/health-checks-dashboard-status.png'; Health checks provide availability and state information about an app. Health checks are often exposed as HTTP endpoints, but can also be used internally by the app to write logs or perform other tasks based on the current health. Health checks are typically used in combination with an external monitoring service or container orchestrator to check the status of an app. -In Aspire, health checks operate in two main contexts: +## Two types of health checks -- **AppHost resource health checks** - Run in the AppHost project to determine resource readiness for orchestration and dependency management. These checks control when dependent resources start and are displayed in the Aspire dashboard. -- **Application health check endpoints** - Run within individual applications and services to expose `/health` and `/alive` endpoints for monitoring and load balancing decisions. +Aspire uses health checks in two distinct contexts. Understanding the difference is crucial: -The data reported by health checks can be used for various scenarios: +| Health check type | Where it runs | What it checks | Used for | +|-------------------|---------------|----------------|----------| +| **AppHost resource checks** | AppHost project | "Is my dependency ready?" | Startup orchestration, `WaitFor()` | +| **Service endpoint checks** | Your application | "Am I healthy?" | Load balancers, Kubernetes probes | -- Influence decisions made by container orchestrators, load balancers, API gateways, and other management services. For instance, if the health check for a containerized app fails, it might be skipped by a load balancer routing traffic. -- Verify that underlying dependencies are available, such as a database or cache, and return an appropriate status message. -- Trigger alerts or notifications when an app isn't responding as expected. + + +## Readiness vs. Liveness + +The two endpoint types serve different purposes: + +- **Readiness (`/health`)** - "Am I ready to receive traffic?" Checks that dependencies are connected, initialization is complete, and the service can handle requests. A failing readiness check means "don't send me traffic yet." + +- **Liveness (`/alive`)** - "Am I still running?" Checks that the process hasn't deadlocked or crashed. A failing liveness check means "restart me." ## Aspire health check endpoints diff --git a/src/frontend/src/content/docs/fundamentals/networking-overview.mdx b/src/frontend/src/content/docs/fundamentals/networking-overview.mdx index c61eb04b..5bfc6a19 100644 --- a/src/frontend/src/content/docs/fundamentals/networking-overview.mdx +++ b/src/frontend/src/content/docs/fundamentals/networking-overview.mdx @@ -14,6 +14,38 @@ import proxyWithDockerPortMapping from '@assets/fundamentals/networking/proxy-wi One of the advantages of developing with Aspire is that it enables you to develop, test, and debug cloud-native apps locally. Inner-loop networking is a key aspect of Aspire that allows your apps to communicate with each other in your development environment. In this article, you learn how Aspire handles various networking scenarios with proxies, endpoints, endpoint configurations, and launch profiles. +## The proxy mental model + +Think of Aspire's networking like a hotel front desk: + +- **Clients** (your code calling APIs) always talk to the **front desk** (proxy) at a known, stable address +- The front desk routes requests to the **actual room** (your service) wherever it happens to be +- If you have **multiple rooms** (replicas), the front desk handles which one to connect to + +This design solves several problems: + +| Problem | How the proxy helps | +|---------|---------------------| +| **Port conflicts** | Two services can't both use port 5000—but the proxy can allocate different ports for each | +| **Replicas** | Multiple instances of a service need load balancing—the proxy handles this automatically | +| **Service restarts** | When a service restarts on a new port, the proxy's address stays the same | + +```mermaid +flowchart LR + Client["Your Code"] + Proxy["Proxy :5000"] + App1["Service :52847"] + App2["Service :52848"] + + Client --> Proxy + Proxy --> App1 + Proxy --> App2 +``` + + + ## Networking in the inner loop The inner loop is the process of developing and testing your app locally before deploying it to a target environment. Aspire provides several tools and features to simplify and enhance the networking experience in the inner loop, such as: @@ -65,6 +97,29 @@ Containers register themselves on the network using their resource name. Aspire [service discovery](/fundamentals/service-discovery/). +### Container network aliases + +By default, containers are accessible on the container network using their **resource name** as a DNS alias. For example, a container added with `AddContainer("mydb", ...)` is reachable at `mydb:5432` from other containers. + +Sometimes you need additional aliases—for example, when a third-party tool expects a specific hostname, or when migrating from an existing Docker Compose setup. Use `WithContainerNetworkAlias` to add custom DNS names: + +```csharp title="AppHost.cs" +var redis = builder.AddRedis("cache") + .WithContainerNetworkAlias("redis-primary") + .WithContainerNetworkAlias("session-store"); +``` + +Now other containers can connect to Redis using any of these names: +- `cache:6379` (default resource name) +- `redis-primary:6379` (custom alias) +- `session-store:6379` (custom alias) + + + ## Launch profiles When you call `AddProject`, the AppHost looks for _Properties/launchSettings.json_ to determine the default set of endpoints. The AppHost selects a specific launch profile using the following rules: @@ -353,3 +408,61 @@ builder.AddProject("apiservice") ``` The preceding code adds a default HTTPS endpoint, as well as an `admin` endpoint on port 19227. However, the `admin` endpoint is excluded from the environment variables. This is useful when you want to expose an endpoint for internal use only. + +## Troubleshooting + +### Port already in use + +**Symptom**: Error message like `Address already in use` or `Failed to bind to port` + +**Common causes**: +- Another instance of your app is still running +- A previous Aspire session didn't shut down cleanly +- Another application is using the same port + +**Solutions**: +1. Stop any running Aspire sessions with `Ctrl+C` or close the dashboard +2. Check for processes using the port: `netstat -ano | findstr :5000` (Windows) or `lsof -i :5000` (macOS/Linux) +3. Let Aspire assign random ports by removing explicit port numbers from `WithEndpoint` + +### Can't connect to container + +**Symptom**: Timeouts or connection refused when connecting to a container resource + +**Common causes**: +- The container hasn't finished starting +- Missing `WaitFor()` dependency +- Container is on the container network but you're connecting from the host + +**Solutions**: +1. Add `.WaitFor(container)` to ensure the container is ready before dependent services start +2. Add `.WithHttpHealthCheck()` or `.WithHealthCheck()` to the container resource +3. Ensure host services use the **exposed port** (not the container's internal port): + +```csharp +// Container exposes port 5432 to host +var db = builder.AddPostgres("db"); + +// Host service connects via exposed port (handled by WithReference) +var api = builder.AddProject("api") + .WithReference(db); // ✅ Correct - uses exposed port +``` + +### Service not discoverable + +**Symptom**: Service discovery fails with "service not found" or DNS resolution errors + +**Solutions**: +1. Verify `WithReference()` is set up between producer and consumer +2. Check the endpoint name in the URI matches exactly (case-sensitive) +3. Review [service discovery troubleshooting](/fundamentals/service-discovery#troubleshooting) + +### HTTPS certificate errors + +**Symptom**: Certificate validation fails in development + +**Solution**: Trust the ASP.NET Core development certificate: + +```bash +dotnet dev-certs https --trust +``` diff --git a/src/frontend/src/content/docs/fundamentals/service-discovery.mdx b/src/frontend/src/content/docs/fundamentals/service-discovery.mdx index 67e564e6..fb009090 100644 --- a/src/frontend/src/content/docs/fundamentals/service-discovery.mdx +++ b/src/frontend/src/content/docs/fundamentals/service-discovery.mdx @@ -5,7 +5,22 @@ description: Understand essential service discovery concepts in Aspire. import { Aside } from '@astrojs/starlight/components'; -In this article, you learn how service discovery works within an Aspire project. Aspire includes functionality for configuring service discovery at development and testing time. Service discovery functionality works by providing configuration in the format expected by the _configuration-based endpoint resolver_ from the Aspire AppHost project to the individual service projects added to the application model. For more information, see [Service discovery in .NET](https://learn.microsoft.com/dotnet/core/extensions/service-discovery). +Service discovery is how your services find each other. Instead of hardcoding URLs like `http://localhost:5001`, your frontend can simply reference the `catalog` service by name—and Aspire handles resolving the actual address. + +## How service discovery works + +When you connect services using `WithReference()`, Aspire sets up automatic service discovery: + +1. **AppHost declares resources** and allocates endpoints +2. **Configuration is injected** into each service via environment variables +3. **Your code uses logical names** like `https+http://catalog` +4. **Aspire's resolver** translates names to actual addresses at runtime + +**The key insight**: Your code uses logical service names (like `catalog`), and Aspire's configuration-based endpoint resolver translates those to actual addresses at runtime. + + ## Implicit service discovery by reference @@ -22,7 +37,14 @@ var frontend = builder.AddProject("frontend") .WithReference(catalog); ``` -In the preceding example, the _frontend_ project references the _catalog_ project and the _basket_ project. The two `WithReference` calls instruct the Aspire project to pass service discovery information for the referenced projects (_catalog_, and _basket_) into the _frontend_ project. +In the preceding example, the _frontend_ project references the _catalog_ project and the _basket_ project. The two `WithReference` calls instruct Aspire to: + +1. **Inject configuration** - Pass service discovery information for `catalog` and `basket` into `frontend` +2. **Enable resolution** - Allow `frontend` to use URIs like `https+http://catalog` in HttpClient + + ## Named endpoints @@ -111,3 +133,54 @@ Similarly, the "dashboard" endpoint can be targeted as follows: builder.Services.AddHttpClient( static client => client.BaseAddress = new("https://_dashboard.basket")); ``` + +## Troubleshooting + +### Service not found or "No endpoints resolved" + +**Symptoms**: HttpClient throws an exception like "No endpoints resolved for service 'myservice'" or connection is refused. + +**Common causes**: +1. **Missing `WithReference()`** - The calling service wasn't connected to the target service in AppHost +2. **Wrong service name** - The name in your URI doesn't match the name in `AddProject()` or `AddContainer()` +3. **Service not started** - The target service failed to start or is still starting + +**Solution**: Verify your AppHost connects the services: + +```csharp +var api = builder.AddProject("api"); +var frontend = builder.AddProject("frontend") + .WithReference(api); // ← This is required! +``` + +### Named endpoint doesn't resolve + +**Symptoms**: Using `https://_endpointName.serviceName` returns an error. + +**Solution**: Verify the endpoint is named in your AppHost: + +```csharp +var api = builder.AddProject("api") + .WithHttpEndpoint(port: 9999, name: "dashboard"); // ← Name must match +``` + +Then use the exact name with underscore prefix: + +```csharp +client.BaseAddress = new("https://_dashboard.api"); +``` + +### Understanding the URI format + +The `https+http://` prefix means "prefer HTTPS, fall back to HTTP". Here's the breakdown: + +| URI Format | Meaning | +|------------|---------| +| `https://catalog` | HTTPS only to default endpoint | +| `http://catalog` | HTTP only to default endpoint | +| `https+http://catalog` | Prefer HTTPS, fall back to HTTP | +| `https://_admin.catalog` | HTTPS to named "admin" endpoint | + + diff --git a/src/frontend/src/content/docs/get-started/aspire-sdk.mdx b/src/frontend/src/content/docs/get-started/aspire-sdk.mdx index 440b26f6..1dc7ead1 100644 --- a/src/frontend/src/content/docs/get-started/aspire-sdk.mdx +++ b/src/frontend/src/content/docs/get-started/aspire-sdk.mdx @@ -82,7 +82,7 @@ For example, if you have two microservices with the same project name (like `Pre This generates `Projects.MicroService1` and `Projects.MicroService2` classes, allowing you to reference each project distinctly in your AppHost: -```csharp +```csharp title="AppHost.cs" var microservice1 = builder.AddProject("micro1"); var microservice2 = builder.AddProject("micro2"); ``` diff --git a/src/frontend/src/content/docs/get-started/first-app.mdx b/src/frontend/src/content/docs/get-started/first-app.mdx index dd69bd02..6c74716b 100644 --- a/src/frontend/src/content/docs/get-started/first-app.mdx +++ b/src/frontend/src/content/docs/get-started/first-app.mdx @@ -212,8 +212,8 @@ To create your first Aspire application, use the [Aspire CLI](/get-started/insta - `CreateBuilder` creates the distributed application builder - `AddProject` registers your API service and web frontend - - `WithReference` creates a connection between services (the web app can call the API) - - `WaitFor` ensures services start in the correct order + - `WithReference` connects services—it injects the API's URL as an environment variable and sets up service discovery so you can use service names instead of hardcoded URLs + - `WaitFor` ensures the API is healthy before starting the frontend, preventing connection errors from race conditions - `WithHttpHealthCheck` monitors service health @@ -244,8 +244,8 @@ To create your first Aspire application, use the [Aspire CLI](/get-started/insta - `AddUvicornApp` adds a Uvicorn-based Python app - `AddUv` adds a Uv environment setup task: `uv sync` - `AddViteApp` registers your React frontend - - `WithReference` connects the frontend to the API - - `WaitFor` ensures the API starts before the frontend + - `WithReference` connects the frontend to the API—it injects the API's URL and sets up service discovery + - `WaitFor` ensures the API is healthy before starting the frontend, preventing connection errors - `PublishWithContainerFiles` bundles the frontend for production deployment @@ -375,4 +375,6 @@ import pythonDashboardDark from '@assets/get-started/python-aspire-dashboard-dar You might be eager to deploy this app next—and we'll show you how Aspire handles that—but you're probably also wondering: "How do I _test_ all this?" Great question! Aspire doesn't just orchestrate locally and deploy, it also helps you test service and resource integrations too. Ready to dive in? [Write your first test](/testing/write-your-first-test/) 💜 + +Having trouble? Check out our [Troubleshooting guide](/get-started/troubleshooting/) for solutions to common problems. \ No newline at end of file diff --git a/src/frontend/src/content/docs/get-started/glossary.mdx b/src/frontend/src/content/docs/get-started/glossary.mdx new file mode 100644 index 00000000..9e8cef92 --- /dev/null +++ b/src/frontend/src/content/docs/get-started/glossary.mdx @@ -0,0 +1,336 @@ +--- +title: Aspire glossary +description: Key terms and concepts used throughout .NET Aspire documentation, including AppHost, resources, service discovery, and orchestration terminology. +--- + +import { Aside } from '@astrojs/starlight/components'; + +This glossary defines the key terms and concepts you'll encounter when working with Aspire. Bookmark this page as a quick reference. + +## Core concepts + +These are the foundational concepts you need to understand when working with Aspire. + +### AppHost + +The **AppHost** is the orchestration project where you define your entire application's architecture in C# code. It's a special Aspire project that: + +- Declares what services, databases, and containers make up your application +- Defines how resources depend on each other +- Configures how resources communicate +- Orchestrates startup order during local development +- Generates deployment manifests for production + +Think of it as the "control tower" for your distributed application. + +```csharp title="AppHost.cs" +// This IS the AppHost - Program.cs in your AppHost project +var builder = DistributedApplication.CreateBuilder(args); + +var db = builder.AddPostgres("db"); +var api = builder.AddProject("api") + .WithReference(db); + +builder.Build().Run(); +``` + +### Resource + +A **resource** is any component that your application depends on. Resources can be: + +| Resource type | Examples | +|--------------|----------| +| **Projects** | .NET applications, services, APIs | +| **Containers** | Docker containers (databases, caches, message brokers) | +| **Executables** | Node.js apps, Python scripts, any executable | +| **Cloud services** | Azure Storage, AWS S3, managed databases | +| **Parameters** | Configuration values, secrets | + +Resources are the building blocks you compose in your AppHost. + +### Distributed application + +A **distributed application**is an application split into multiple independent services that communicate over a network. Instead of one monolithic application, you have: + +- A frontend service +- One or more API services +- Databases +- Caches +- Message queues +- etc. + +Aspire helps you orchestrate all these pieces together. + +### Service defaults + +**Service defaults**are pre-configured settings that Aspire applies to your .NET projects automatically. They include: + +- **OpenTelemetry** - Automatic logging, tracing, and metrics +- **Health checks** - Endpoints for monitoring (`/health`, `/alive`) +- **Service discovery** - Automatic resolution of service URLs +- **Resilience** - Retry policies for HTTP calls + +When you add `builder.AddServiceDefaults()` to a project, you get production-ready observability and resilience out of the box. + +--- + +## APIs and patterns + +These are the key APIs and patterns you'll use when building Aspire applications. + +### WithReference + +`WithReference()` is how you connect resources together. When you call `.WithReference(otherResource)`, Aspire: + +1. Injects the connection information as environment variables +2. Sets up service discovery so your code can find the other resource +3. Creates a dependency relationship for startup ordering + +```csharp title="AppHost.cs" +var db = builder.AddPostgres("db"); +var api = builder.AddProject("api") + .WithReference(db); // API now has DATABASE connection info injected +``` + +The API project will receive environment variables like: +- `ConnectionStrings__db` - The database connection string +- Service discovery configuration for the "db" resource + +### WaitFor + +`WaitFor()` tells Aspire to delay starting a resource until its dependency is ready: + +```csharp title="AppHost.cs" +var db = builder.AddPostgres("db"); +var api = builder.AddProject("api") + .WithReference(db) + .WaitFor(db); // Don't start API until database is healthy +``` + + + +### WaitForCompletion + +`WaitForCompletion()` waits for a resource to finish and exit (not just start). Useful for setup scripts: + +```csharp title="AppHost.cs" +var migrate = builder.AddProject("migrate"); +var api = builder.AddProject("api") + .WaitForCompletion(migrate); // Wait for migrations to complete +``` + +### WaitForStart + +`WaitForStart()` waits only for a resource to reach the running state, without waiting for health checks to pass: + +```csharp title="AppHost.cs" +var db = builder.AddPostgres("db"); +var api = builder.AddProject("api") + .WaitForStart(db); // Start as soon as db is running, don't wait for healthy +``` + + + +--- + +## Key terms + +These terms appear frequently throughout the documentation and in error messages. + +### Connection string + +A **connection string** is a formatted string containing the information needed to connect to a resource (database, cache, message broker, etc.). In Aspire, connection strings are: + +- Automatically generated by the AppHost based on resource configuration +- Injected into your application as environment variables +- Retrieved using `builder.Configuration.GetConnectionString("resource-name")` + +Example: `Host=localhost;Port=5432;Database=mydb;Username=postgres;Password=secret` + +### Service discovery + +**Service discovery**is the mechanism that allows your services to find and communicate with each other by name, without hardcoding addresses. In Aspire: + +- Resources are addressable by their resource name (e.g., `http://apiservice`) +- The AppHost configures DNS/environment variables so services can resolve each other +- Works automatically when you use `WithReference()` + +For details, see [Service Discovery](/fundamentals/service-discovery/). + +### Health check + +A **health check**is a mechanism to determine if a resource is ready and functioning. Aspire uses health checks in two ways: + +1. **AppHost level**: Determines when dependencies are ready (controls `WaitFor()` behavior) +2. **Application level**: Exposes `/health` and `/alive` endpoints for load balancers + +For details, see [Health Checks](/fundamentals/health-checks/). + +### Environment variable + +Aspire uses **environment variables**to pass configuration from the AppHost to your services: + +- Connection strings: `ConnectionStrings__resourcename` +- Service endpoints: `services__servicename__https__0` +- Custom values via `WithEnvironment()` + +Your application reads these using standard .NET configuration (`IConfiguration`). + +--- + +## Resource types + +Aspire uses two types of NuGet packages to work with resources. + +### Hosting integration + +A **hosting integration** is an Aspire package that helps you add and configure resources in your AppHost. Hosting integrations: + +- Add containers or cloud resources to your app model +- Configure networking, health checks, and volumes +- Handle connection string generation + +Example: `Aspire.Hosting.PostgreSQL` adds the `AddPostgres()` method. + +### Client integration + +A **client integration**is an Aspire package that helps your application code connect to resources. Client integrations: + +- Register SDK clients with dependency injection +- Configure connection strings from environment variables +- Add health checks and telemetry + +Example: `Aspire.Npgsql` registers `NpgsqlConnection` for PostgreSQL. + +### The relationship + +``` +┌─────────────────────────────────────────────────────────────┐ +│ AppHost (uses Hosting Integration) │ +│ Aspire.Hosting.PostgreSQL → AddPostgres("db") │ +│ ↓ │ +│ Injects connection info │ +└─────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ API Project (uses Client Integration) │ +│ Aspire.Npgsql → builder.AddNpgsqlDataSource("db") │ +│ ↓ │ +│ Reads connection from env vars │ +│ Registers NpgsqlConnection in DI │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Execution modes + +Aspire operates in two distinct modes depending on what you're trying to accomplish. + +### Run mode + +**Run mode** is local development. When you execute `aspire run` or debug your AppHost: + +- Aspire starts all your resources locally +- Containers run in Docker +- The Aspire Dashboard shows logs and traces +- Service discovery uses localhost addresses + +### Publish mode + +**Publish mode**generates deployment artifacts. When you execute `aspire publish`: + +- Aspire generates Kubernetes manifests, Docker Compose files, or cloud infrastructure +- No containers are started locally +- Connection strings reference production endpoints +- Output is ready for CI/CD pipelines + +--- + +## Dashboard and observability + +Aspire provides built-in tools for monitoring and debugging your applications. + +### Aspire dashboard + +The **Aspire Dashboard** is a web UI that automatically runs during local development. It shows: + +- All your resources and their status +- Real-time logs from every service +- Distributed traces across service calls +- Metrics and performance data +- Resource health checks + +Access it at the URL shown when you run `aspire run` (typically `http://localhost:15888`). + +### OpenTelemetry + +**OpenTelemetry** (OTEL) is the observability standard that Aspire uses for: + +- **Logs** - Structured logging with context +- **Traces** - Following requests across services +- **Metrics** - Performance measurements + +Aspire configures OpenTelemetry automatically through service defaults. + +--- + +## Common patterns + +These patterns appear frequently in Aspire applications. + +### Emulator pattern + +Many hosting integrations support running as an **emulator** for local development: + +```csharp title="AppHost.cs" +var storage = builder.AddAzureStorage("storage") + .RunAsEmulator(); // Uses Azurite container locally +``` + +This lets you develop against Azure, AWS, or other cloud services without needing actual cloud accounts during development. + +### Existing resource pattern + +Connect to resources that already exist (not managed by Aspire): + +```csharp title="AppHost.cs" +var existingDb = builder.AddPostgres("db") + .AsExisting(name, resourceGroup); +``` + +Use this for production databases or shared team resources. + +--- + +## API reference terms + +These terms appear frequently in API documentation and advanced usage: + +| Term | Description | +|------|-------------| +| `IResourceAnnotation` | Typed metadata object attached to resources. | +| `WithAnnotation()` | Fluent method to attach typed annotations. | +| `ReferenceExpression` | Structured formatter preserving value references. | +| `ResourceNotificationService` | Publishes observable state updates. | + +| Concept | Definition | +|---------|------------| +| DAG | Directed acyclic graph—the structure of resource dependencies. | +| Heterogeneous DAG | DAG containing varied resource types (projects, containers, cloud). | +| Publisher | Component that emits deployment artifacts from the app model. | +| Hoisting | Leaving a value unresolved for later substitution at runtime. | +| Deferred evaluation | Computing a value only when needed. | +| Lifecycle events | Time-based signals for resource transitions. | + +--- + +## See also + +- [What is Aspire?](/get-started/what-is-aspire/) +- [Create your first Aspire app](/get-started/first-app/) +- [AppHost overview](/get-started/app-host/) diff --git a/src/frontend/src/content/docs/get-started/troubleshooting.mdx b/src/frontend/src/content/docs/get-started/troubleshooting.mdx new file mode 100644 index 00000000..db0a1127 --- /dev/null +++ b/src/frontend/src/content/docs/get-started/troubleshooting.mdx @@ -0,0 +1,164 @@ +--- +title: Aspire troubleshooting guide +description: Solutions to common problems when getting started with .NET Aspire, including Docker issues, port conflicts, and service discovery errors. +--- + +import { Aside } from '@astrojs/starlight/components'; +import { Kbd } from 'starlight-kbd/components'; +import LearnMore from '@components/LearnMore.astro'; +import Expand from '@components/Expand.astro'; + +Having issues getting started with Aspire? This page covers solutions to the most common problems developers encounter. + +## Docker Desktop not running + +**Symptoms**: `aspire run` hangs or shows "waiting for container runtime" messages. + +**Solution**: Start Docker Desktop (or Podman) before running `aspire run`. Check your system tray to confirm Docker is running with a green status indicator. + + + +## Port already in use + +**Symptoms**: Error message like "Address already in use" or "Port 5000 is already bound." + +**Solution**: +- Stop any other applications using the same ports +- If you have another Aspire app running, stop it first with + + + +**Windows:** +```powershell +netstat -ano | findstr :5000 +# Find the PID in the last column, then: +taskkill /PID /F +``` + +**macOS/Linux:** +```bash +lsof -i :5000 +# Then kill the process: +kill -9 +``` + + + +## Service shows "Unhealthy" in dashboard + +**Symptoms**: A service has a red status or shows "Unhealthy" in the Aspire dashboard. + +**Solution**: +1. Click on the service name in the dashboard to view its logs +2. Look for startup errors or exceptions +3. Verify the health check endpoint exists (e.g., `/health` returns a 200 OK) +4. Check that all dependencies started successfully + +## "Connection refused" errors + +**Symptoms**: Your frontend can't connect to the API, showing connection refused errors. + +**Solution**: Make sure you're using `WaitFor()` in your AppHost: + +```csharp title="AppHost.cs" +builder.AddProject("frontend") + .WithReference(apiService) + .WaitFor(apiService); // Add this line! +``` + +## Environment variables not available + +**Symptoms**: Your service can't find connection strings or configuration that should be injected. + +**Solution**: Verify you're using `WithReference()` to connect resources. + + + +In your AppHost: + +```csharp title="AppHost.cs" +var db = builder.AddPostgres("db"); +var api = builder.AddProject("api") + .WithReference(db); // This injects the connection string +``` + +In your service code, access the connection string via configuration: + +```csharp title="Program.cs" +var connectionString = builder.Configuration.GetConnectionString("db"); +``` + +The connection string is injected as an environment variable named `ConnectionStrings__db`. + + + +## Container image pull failures + +**Symptoms**: Error messages about failing to pull container images, or "image not found" errors. + +**Solution**: +- Check your internet connection +- Verify Docker Hub or your container registry is accessible +- If using a corporate network, check proxy settings in Docker Desktop + + + +```bash +# Try pulling the image manually +docker pull redis:latest + +# If behind a proxy, configure Docker Desktop: +# Settings > Resources > Proxies > Manual proxy configuration +``` + + + +## Dashboard not loading + +**Symptoms**: The Aspire dashboard URL doesn't respond or shows a blank page. + +**Solution**: +1. Check the console output for the correct dashboard URL (it may use a different port) +2. Ensure no browser extensions are blocking the page +3. Try a different browser or incognito mode +4. Check if antivirus or firewall is blocking the connection + +## Service discovery not working + +**Symptoms**: Services can't find each other by name (e.g., `http://apiservice` doesn't resolve). + +**Solution**: Ensure you're using the service name exactly as defined in your AppHost. + + + +```csharp title="AppHost.cs" +// In AppHost +var api = builder.AddProject("apiservice"); // Name is "apiservice" +``` + +```csharp title="Program.cs" +// In your consuming service, use exactly this name +var client = new HttpClient { BaseAddress = new Uri("http://apiservice") }; +``` + +Also verify: +- Both services have `AddServiceDefaults()` called +- The consuming service has `.WithReference(api)` in the AppHost + + + + +For a deeper understanding of how service discovery works in Aspire, see [Service discovery](/fundamentals/service-discovery/). + + + +For more help, see [Aspire Support](/support/) or check the [GitHub Discussions](https://github.com/dotnet/aspire/discussions). + + +## See also + +- [Create your first Aspire app](/get-started/first-app/) +- [Health checks](/fundamentals/health-checks/) +- [Service discovery](/fundamentals/service-discovery/) diff --git a/src/frontend/src/content/docs/get-started/what-is-aspire.mdx b/src/frontend/src/content/docs/get-started/what-is-aspire.mdx index 6dba29b6..0b2c1e3a 100644 --- a/src/frontend/src/content/docs/get-started/what-is-aspire.mdx +++ b/src/frontend/src/content/docs/get-started/what-is-aspire.mdx @@ -5,11 +5,91 @@ tableOfContents: true lastUpdated: true --- -import { Aside } from '@astrojs/starlight/components'; +import { Aside, Steps } from '@astrojs/starlight/components'; import LearnMore from '@components/LearnMore.astro'; Aspire streamlines building, running, debugging, and deploying distributed apps. Picture your app as a set of services, databases, and frontends—when they're deployed, they all work together seamlessly, but every time you develop them they need to be individually started and connected. With Aspire, you get a unified toolchain that eliminates complex configs and makes local debugging effortless. Instantly launch and debug your entire app with a single command. Ready to deploy? Aspire lets you publish anywhere—Kubernetes, the cloud, or your own servers. It's also fully extensible, so you can integrate your favorite tools and services with ease. +## Why Aspire? + +Building modern applications means juggling multiple services, databases, and dependencies. Here's what that looks like **without** Aspire: + +### The pain of distributed development + +| Problem | Without Aspire | With Aspire | +|---------|----------------|-------------| +| **Starting services** | Open 5 terminals, run 5 different commands, hope they start in the right order | Run `aspire run` — everything starts automatically. See [AppHost overview](/get-started/app-host/). | +| **Connection strings** | Hardcode `localhost:5432` everywhere, break production deploys | Service discovery handles it — same code works locally and in production. See [Service discovery](/fundamentals/service-discovery/). | +| **"Works on my machine"** | Different team members have different ports, different configs | One AppHost definition, everyone runs the same setup. See [First app tutorial](/get-started/first-app/). | +| **Debugging** | Attach debugger to each service individually | Debug your entire stack with a single F5. See [Debugging overview](/fundamentals/debugging/). | +| **Finding logs** | Check 5 different terminal windows | Aspire Dashboard shows all logs in one place. See [Dashboard overview](/dashboard/overview/). | +| **Adding a database** | Install locally, configure connection, hope versions match | `builder.AddPostgres("db")` — containerized, versioned, consistent. See [Integrations](/integrations/overview/). | + +### Before and after + +**Without Aspire**, your startup routine might look like: + + + +1. Start the database container + + ```bash frame="terminal" data-disable-copy + docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres:15 + ``` + +2. Start the C# API service + + ```bash frame="terminal" data-disable-copy + cd api && dotnet run + ``` + +3. Start the Python worker + + ```bash frame="terminal" data-disable-copy + cd worker && source .venv/bin/activate && python main.py + ``` + +4. Start the JavaScript frontend + + ```bash frame="terminal" data-disable-copy + cd frontend && npm run dev + ``` + +5. Manually verify they can all connect... + + Frustration ensues. 🙁 + + + +**With Aspire**, it's just: + +```bash +aspire run +``` + +And your AppHost defines everything in type-safe C#—even for polyglot stacks: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var db = builder.AddPostgres("db"); + +var api = builder.AddProject("api") + .WithReference(db); + +var worker = builder.AddPythonApp("worker", "../worker", "main.py") + .WithReference(db); + +builder.AddNpmApp("frontend", "../frontend") + .WithReference(api); + +builder.Build().Run(); +``` + + + ## Key benefits - **Unified Development Experience**: Launch and debug your entire distributed application with a single command. diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-application-insights.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-application-insights.mdx new file mode 100644 index 00000000..fe2aa398 --- /dev/null +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-application-insights.mdx @@ -0,0 +1,260 @@ +--- +title: Azure Application Insights +description: Learn how to use the Azure Application Insights hosting integration to add application performance monitoring and telemetry to your Aspire application. +--- + +import { Aside } from '@astrojs/starlight/components'; +import InstallPackage from '@components/InstallPackage.astro'; +import { Image } from 'astro:assets'; +import appInsightsIcon from '@assets/icons/azure-appinsights-icon.png'; + +Azure Application Insights logo + +[Azure Application Insights](https://azure.microsoft.com/services/monitor/) is an extensible Application Performance Management (APM) service for developers and DevOps professionals. The Aspire Azure Application Insights integration enables you to provision Application Insights resources for collecting telemetry data from your applications. + +## Hosting integration + +The Aspire Azure Application Insights hosting integration models the Application Insights component as the following type: + +- `AzureApplicationInsightsResource`: Represents an Azure Application Insights resource. + +To access this type and APIs, add the [📦 Aspire.Hosting.Azure.ApplicationInsights](https://www.nuget.org/packages/Aspire.Hosting.Azure.ApplicationInsights) NuGet package to your AppHost project. + + + +### Add Azure Application Insights resource + +In your AppHost project, call `AddAzureApplicationInsights` to add and return an Azure Application Insights resource builder: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var appInsights = builder.AddAzureApplicationInsights("app-insights"); + +builder.AddProject() + .WithReference(appInsights); + +// After adding all resources, run the app... +``` + +The preceding code: + +- Adds an Azure Application Insights resource named `app-insights` to the application model. +- Automatically creates a Log Analytics workspace to store the telemetry data. +- References the Application Insights resource in the `ExampleProject`, making the connection string available. + + + +### Use with existing Log Analytics workspace + +If you have an existing Log Analytics workspace that you want to use, you can link Application Insights to it: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("log-analytics"); +var appInsights = builder.AddAzureApplicationInsights("app-insights", logAnalytics); + +builder.AddProject() + .WithReference(appInsights); + +// After adding all resources, run the app... +``` + +The preceding code: + +- Adds an Azure Log Analytics workspace resource named `log-analytics`. +- Adds an Azure Application Insights resource named `app-insights` that uses the specified Log Analytics workspace. +- References the Application Insights resource in the `ExampleProject`. + +### Configure Log Analytics workspace after creation + +You can also configure the Log Analytics workspace using the fluent API: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("log-analytics"); +var appInsights = builder.AddAzureApplicationInsights("app-insights") + .WithLogAnalyticsWorkspace(logAnalytics); + +builder.AddProject() + .WithReference(appInsights); + +// After adding all resources, run the app... +``` + +### Connection properties + +The Azure Application Insights resource exposes the following connection property: + +| Property name | Description | +|---------------|-------------| +| `appInsightsConnectionString` | The connection string for Application Insights. | + +This connection string is automatically made available to referencing projects as an environment variable named `APPLICATIONINSIGHTS_CONNECTION_STRING` (or based on the resource name). + +### Provisioning-generated Bicep + +If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by-hand—the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure Application Insights resource, the following Bicep is generated: + +```bicep title="Generated Bicep — app-insights.bicep" +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param applicationType string = 'web' + +param kind string = 'web' + +param logAnalyticsWorkspaceId string + +resource app_insights 'Microsoft.Insights/components@2020-02-02' = { + name: take('app-insights-${uniqueString(resourceGroup().id)}', 260) + kind: kind + location: location + properties: { + Application_Type: applicationType + WorkspaceResourceId: logAnalyticsWorkspaceId + } + tags: { + 'aspire-resource-name': 'app-insights' + } +} + +output appInsightsConnectionString string = app_insights.properties.ConnectionString +``` + +The preceding Bicep provisions an Azure Application Insights component linked to a Log Analytics workspace for storing telemetry data. + +The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files. + +#### Customize provisioning infrastructure + +All Aspire Azure resources are subclasses of the `AzureProvisioningResource` type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources using the `ConfigureInfrastructure` API. For example, you can configure the application type, retention settings, and more. The following example demonstrates how to customize the Azure Application Insights resource: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var appInsights = builder.AddAzureApplicationInsights("app-insights") + .ConfigureInfrastructure(infra => + { + var insights = infra.GetProvisionableResources() + .OfType() + .Single(); + + insights.RetentionInDays = 90; + insights.IngestionMode = ComponentIngestionMode.LogAnalytics; + insights.Tags.Add("environment", "production"); + }); + +builder.AddProject() + .WithReference(appInsights); +``` + +The preceding code: + +- Chains a call to the `ConfigureInfrastructure` API: + - The `infra` parameter is an instance of the `AzureResourceInfrastructure` type. + - The provisionable resources are retrieved by calling `GetProvisionableResources`. + - The `ApplicationInsightsComponent` is configured with 90-day retention and a custom tag. + +For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.ApplicationInsights](https://learn.microsoft.com/dotnet/api/azure.provisioning.applicationinsights) API documentation. + +## Client integration + + + +### Configure Application Insights in your application + +To use Application Insights in your application, configure the OpenTelemetry exporter in your service defaults: + +```csharp title="C# — Extensions.cs" +public static IHostApplicationBuilder AddServiceDefaults( + this IHostApplicationBuilder builder) +{ + builder.ConfigureOpenTelemetry(); + + // ... other configuration + + return builder; +} + +public static IHostApplicationBuilder ConfigureOpenTelemetry( + this IHostApplicationBuilder builder) +{ + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; +} + +private static IHostApplicationBuilder AddOpenTelemetryExporters( + this IHostApplicationBuilder builder) +{ + var useOtlpExporter = !string.IsNullOrWhiteSpace( + builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry() + .UseOtlpExporter(); + } + + // Application Insights will automatically pick up the connection string + // from the APPLICATIONINSIGHTS_CONNECTION_STRING environment variable + + return builder; +} +``` + +Alternatively, you can use the Azure Monitor OpenTelemetry exporter directly: + +```csharp title="C# — Program.cs" +builder.Services.AddOpenTelemetry() + .UseAzureMonitor(options => + { + options.ConnectionString = builder.Configuration + .GetConnectionString("app-insights"); + }); +``` + +## See also + +- [Application Insights overview](https://learn.microsoft.com/azure/azure-monitor/app/app-insights-overview) +- [Azure Monitor OpenTelemetry for .NET](https://learn.microsoft.com/azure/azure-monitor/app/opentelemetry-enable) +- [Azure Log Analytics integration](/integrations/cloud/azure/azure-log-analytics/) +- [Azure integration overview](/integrations/cloud/azure/overview/) diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-data-explorer.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-data-explorer.mdx new file mode 100644 index 00000000..28cc66d6 --- /dev/null +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-data-explorer.mdx @@ -0,0 +1,308 @@ +--- +title: Azure Data Explorer (Kusto) +description: Learn how to use the Azure Data Explorer (Kusto) hosting integration to add Kusto clusters and databases to your Aspire application. +--- + +import { Aside } from '@astrojs/starlight/components'; +import InstallPackage from '@components/InstallPackage.astro'; +import { Image } from 'astro:assets'; +import kustoIcon from '@assets/icons/azure-dataexplorer-icon.svg'; + +Azure Data Explorer logo + +[Azure Data Explorer](https://azure.microsoft.com/services/data-explorer/) (also known as Kusto) is a fast and highly scalable data exploration service for log and telemetry data. The Aspire Azure Data Explorer integration enables you to provision Kusto clusters and databases, or run a local emulator during development. + +## Hosting integration + +The Aspire Azure Data Explorer hosting integration models Kusto resources as the following types: + +- `AzureKustoClusterResource`: Represents an Azure Data Explorer (Kusto) cluster resource. +- `AzureKustoReadWriteDatabaseResource`: Represents an Azure Data Explorer database resource. +- `AzureKustoEmulatorResource`: Represents an Azure Data Explorer emulator resource. + +To access these types and APIs, add the [📦 Aspire.Hosting.Azure.Kusto](https://www.nuget.org/packages/Aspire.Hosting.Azure.Kusto) NuGet package to your AppHost project. + + + +### Add Azure Data Explorer cluster resource + +In your AppHost project, call `AddAzureKustoCluster` to add and return an Azure Data Explorer cluster resource builder: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var kusto = builder.AddAzureKustoCluster("kusto"); + +builder.AddProject() + .WithReference(kusto); + +// After adding all resources, run the app... +``` + +The preceding code adds an Azure Data Explorer cluster resource named `kusto` to the application model. + + + +### Add Azure Data Explorer database + +To add a database to a Kusto cluster, call the `AddReadWriteDatabase` method on the cluster resource builder: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var kusto = builder.AddAzureKustoCluster("kusto"); +var database = kusto.AddReadWriteDatabase("analytics"); + +builder.AddProject() + .WithReference(database) + .WaitFor(database); + +// After adding all resources, run the app... +``` + +The preceding code: + +- Adds an Azure Data Explorer cluster resource named `kusto`. +- Adds a database named `analytics` to the cluster. +- References the database in the `ExampleProject` and waits for it to be ready. + +### Run Azure Data Explorer as an emulator + +For local development and testing, you can run Azure Data Explorer as an emulator using the Kustainer container. Call the `RunAsEmulator` method: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var kusto = builder.AddAzureKustoCluster("kusto") + .RunAsEmulator(); + +var database = kusto.AddReadWriteDatabase("analytics"); + +builder.AddProject() + .WithReference(database) + .WaitFor(database); + +// After adding all resources, run the app... +``` + +When running as an emulator: + +- The Kustainer Docker container is used to emulate Azure Data Explorer. +- Databases are automatically created when the emulator starts. +- The connection string is automatically configured for local development. + +### Configure the emulator port + +By default, the emulator uses a dynamic port. To configure a specific host port: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var kusto = builder.AddAzureKustoCluster("kusto") + .RunAsEmulator(emulator => + { + emulator.WithHostPort(8080); + }); + +// After adding all resources, run the app... +``` + +### Add database creation script + +When running as an emulator, you can provide a KQL script to initialize the database with tables and data: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var kusto = builder.AddAzureKustoCluster("kusto") + .RunAsEmulator(); + +var database = kusto.AddReadWriteDatabase("analytics") + .WithCreationScript(""" + .create table Events ( + Timestamp: datetime, + EventType: string, + Message: string + ) + """); + +builder.AddProject() + .WithReference(database) + .WaitFor(database); + +// After adding all resources, run the app... +``` + + + +## Client integration + + + +### Connect to Azure Data Explorer from your application + +To connect to Azure Data Explorer from your application, use the connection string provided by Aspire: + +```csharp title="C# — Program.cs" +var connectionString = builder.Configuration.GetConnectionString("analytics"); + +var kcsb = new KustoConnectionStringBuilder(connectionString) + .WithAadAzureTokenCredentialsAuthentication(new DefaultAzureCredential()); + +using var client = KustoClientFactory.CreateCslQueryProvider(kcsb); +``` + +When running against the emulator, no authentication is required: + +```csharp title="C# — Program.cs" +var connectionString = builder.Configuration.GetConnectionString("analytics"); +var kcsb = new KustoConnectionStringBuilder(connectionString); + +using var client = KustoClientFactory.CreateCslQueryProvider(kcsb); +``` + +## Dashboard commands + +When an Azure Data Explorer cluster resource is running, the Aspire dashboard provides commands to open the Kusto Explorer: + +- **Open in Kusto Explorer (Desktop)**: Opens the desktop Kusto Explorer application (Windows only). +- **Open in Kusto Explorer (Web)**: Opens the web-based Kusto Explorer (Azure deployments only). + +## Connection string and properties + +When you add an Azure Data Explorer resource to your application, the connection properties are made available to consuming resources. The following properties are available: + +### Cluster resource + +| Property | Description | +|----------|-------------| +| `Uri` | The URI of the Azure Data Explorer cluster endpoint. | + +### Database resource + +| Property | Description | +|----------|-------------| +| `Uri` | The URI of the parent cluster endpoint (inherited from cluster). | +| `DatabaseName` | The name of the database. | + +The connection string format is: `Data Source=https://{cluster-uri};Initial Catalog={database-name}` + +### Provisioning-generated Bicep + +If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by-hand—the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure Data Explorer resource, the following Bicep is generated: + +```bicep title="Generated Bicep — kusto.bicep" +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param principalId string + +param principalName string + +resource kusto 'Microsoft.Kusto/clusters@2024-04-13' = { + name: take('kusto${uniqueString(resourceGroup().id)}', 22) + location: location + sku: { + name: 'Dev(No SLA)_Standard_E2a_v4' + tier: 'Basic' + capacity: 1 + } + properties: { + enableAutoStop: true + } + tags: { + 'aspire-resource-name': 'kusto' + } +} + +resource analytics 'Microsoft.Kusto/clusters/databases@2024-04-13' = { + name: 'analytics' + location: location + kind: 'ReadWrite' + parent: kusto +} + +resource kusto_principalAssignment 'Microsoft.Kusto/clusters/principalAssignments@2024-04-13' = { + name: guid(kusto.id, principalId, 'AllDatabasesAdmin') + properties: { + principalId: principalId + principalType: 'App' + role: 'AllDatabasesAdmin' + } + parent: kusto +} + +output kustoClusterUri string = kusto.properties.uri +``` + +The preceding Bicep provisions an Azure Data Explorer cluster with a dev/test SKU and a read-write database. + +The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files. + +#### Customize provisioning infrastructure + +All Aspire Azure resources are subclasses of the `AzureProvisioningResource` type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources using the `ConfigureInfrastructure` API. For example, you can configure the cluster SKU, capacity, and more. The following example demonstrates how to customize the Azure Data Explorer resource: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var kusto = builder.AddAzureKustoCluster("kusto") + .ConfigureInfrastructure(infra => + { + var cluster = infra.GetProvisionableResources() + .OfType() + .Single(); + + cluster.Sku = new KustoSku + { + Name = KustoSkuName.StandardE8aV4, + Tier = KustoSkuTier.Standard, + Capacity = 2 + }; + cluster.IsAutoStopEnabled = false; + cluster.Tags.Add("environment", "production"); + }) + .AddReadWriteDatabase("analytics"); +``` + +The preceding code: + +- Chains a call to the `ConfigureInfrastructure` API: + - The `infra` parameter is an instance of the `AzureResourceInfrastructure` type. + - The provisionable resources are retrieved by calling `GetProvisionableResources`. + - The `KustoCluster` is configured with a Standard SKU, 2-node capacity, and auto-stop disabled. + +For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.Kusto](https://learn.microsoft.com/dotnet/api/azure.provisioning.kusto) API documentation. + +## Health checks + +The Azure Data Explorer hosting integration automatically adds health checks for both cluster and database resources. The health check verifies that: + +- The cluster is reachable and responding to queries +- When using a database, the database exists and is accessible + +Health check status is displayed in the Aspire dashboard resource list. + +## See also + +- [Azure Data Explorer documentation](https://learn.microsoft.com/azure/data-explorer/) +- [Kusto Query Language (KQL) overview](https://learn.microsoft.com/azure/data-explorer/kusto/query/) +- [Kusto .NET SDK](https://learn.microsoft.com/azure/data-explorer/kusto/api/netfx/about-the-sdk) +- [Azure integration overview](/integrations/cloud/azure/overview/) diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-log-analytics.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-log-analytics.mdx new file mode 100644 index 00000000..29dec8ce --- /dev/null +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-log-analytics.mdx @@ -0,0 +1,157 @@ +--- +title: Azure Log Analytics +description: Learn how to use the Azure Log Analytics hosting integration to add Log Analytics workspaces to your Aspire application for centralized logging and monitoring. +--- + +import { Aside } from '@astrojs/starlight/components'; +import InstallPackage from '@components/InstallPackage.astro'; +import { Image } from 'astro:assets'; +import logAnalyticsIcon from '@assets/icons/azure-loganalytics-icon.png'; + +Azure Log Analytics logo + +[Azure Log Analytics](https://azure.microsoft.com/services/monitor/) is a tool in Azure Monitor that allows you to edit and run log queries against data in Azure Monitor Logs. The Aspire Azure Log Analytics integration enables you to provision Log Analytics workspaces for centralized logging and monitoring. + +## Hosting integration + +The Aspire Azure Log Analytics hosting integration models the Log Analytics workspace as the following type: + +- `AzureLogAnalyticsWorkspaceResource`: Represents an Azure Log Analytics workspace resource. + +To access this type and APIs, add the [📦 Aspire.Hosting.Azure.OperationalInsights](https://www.nuget.org/packages/Aspire.Hosting.Azure.OperationalInsights) NuGet package to your AppHost project. + + + +### Add Azure Log Analytics workspace resource + +In your AppHost project, call `AddAzureLogAnalyticsWorkspace` to add and return an Azure Log Analytics workspace resource builder: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("log-analytics"); + +// After adding all resources, run the app... +``` + +The preceding code adds an Azure Log Analytics workspace resource named `log-analytics` to the application model. + + + +### Use with Application Insights + +A common pattern is to use a Log Analytics workspace with Application Insights for centralized telemetry collection. You can link an Application Insights resource to a Log Analytics workspace: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("log-analytics"); +var appInsights = builder.AddAzureApplicationInsights("app-insights", logAnalytics); + +builder.AddProject() + .WithReference(appInsights); + +// After adding all resources, run the app... +``` + +The preceding code: + +- Adds an Azure Log Analytics workspace resource named `log-analytics`. +- Adds an Azure Application Insights resource named `app-insights` that uses the Log Analytics workspace for log storage. +- References the Application Insights resource in the `ExampleProject`. + +For more information, see [Azure Application Insights integration](/integrations/cloud/azure/azure-application-insights/). + +### Connection properties + +The Azure Log Analytics workspace resource exposes the following connection property: + +| Property name | Description | +|---------------|-------------| +| `logAnalyticsWorkspaceId` | The resource ID of the Log Analytics workspace. | + +### Provisioning-generated Bicep + +If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by-hand—the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure Log Analytics workspace resource, the following Bicep is generated: + +```bicep title="Generated Bicep — log-analytics.bicep" +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +resource log_analytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { + name: take('log-analytics-${uniqueString(resourceGroup().id)}', 63) + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } + tags: { + 'aspire-resource-name': 'log-analytics' + } +} + +output logAnalyticsWorkspaceId string = log_analytics.id +``` + +The preceding Bicep provisions an Azure Log Analytics workspace with the pay-per-GB pricing tier. + +The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files. + +#### Customize provisioning infrastructure + +All Aspire Azure resources are subclasses of the `AzureProvisioningResource` type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources using the `ConfigureInfrastructure` API. For example, you can configure the SKU, retention period, and more. The following example demonstrates how to customize the Azure Log Analytics workspace: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("log-analytics") + .ConfigureInfrastructure(infra => + { + var workspace = infra.GetProvisionableResources() + .OfType() + .Single(); + + workspace.Sku = new OperationalInsightsWorkspaceSku( + OperationalInsightsWorkspaceSkuName.PerGB2018); + workspace.RetentionInDays = 90; + workspace.Tags.Add("environment", "production"); + }); + +var appInsights = builder.AddAzureApplicationInsights("app-insights", logAnalytics); +``` + +The preceding code: + +- Chains a call to the `ConfigureInfrastructure` API: + - The `infra` parameter is an instance of the `AzureResourceInfrastructure` type. + - The provisionable resources are retrieved by calling `GetProvisionableResources`. + - The `OperationalInsightsWorkspace` is configured with 90-day retention and a custom tag. + +For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.OperationalInsights](https://learn.microsoft.com/dotnet/api/azure.provisioning.operationalinsights) API documentation. + +## Client integration + + + +## See also + +- [Azure Monitor Logs overview](https://learn.microsoft.com/azure/azure-monitor/logs/data-platform-logs) +- [Log Analytics workspace overview](https://learn.microsoft.com/azure/azure-monitor/logs/log-analytics-workspace-overview) +- [Azure Application Insights integration](/integrations/cloud/azure/azure-application-insights/) +- [Azure integration overview](/integrations/cloud/azure/overview/) diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-sql-database.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-sql-database.mdx index 82675e5d..fc968c61 100644 --- a/src/frontend/src/content/docs/integrations/cloud/azure/azure-sql-database.mdx +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-sql-database.mdx @@ -95,6 +95,111 @@ The preceding code configures an Azure SQL Database resource to run locally in a > [!TIP] > The `RunAsContainer` method is useful for local development and testing. The API exposes an optional delegate that enables you to customize the underlying `SqlServerServerResource` configuration. For example, you can add a data volume or data bind mount. For more information, see the [Aspire SQL Server hosting integration](/integrations/databases/sql-server/sql-server-host/#add-sql-server-resource-with-data-volume) section. +### Provisioning-generated Bicep + +If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by-hand—the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure SQL Server resource, the following Bicep is generated: + +```bicep title="Generated Bicep — azuresql.bicep" +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param principalId string + +param principalName string + +resource sqlServerAdminManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: take('azuresql-admin-${uniqueString(resourceGroup().id)}', 63) + location: location +} + +resource azuresql 'Microsoft.Sql/servers@2024-05-01-preview' = { + name: take('azuresql-${uniqueString(resourceGroup().id)}', 63) + location: location + properties: { + administrators: { + administratorType: 'ActiveDirectory' + login: sqlServerAdminManagedIdentity.name + sid: sqlServerAdminManagedIdentity.properties.principalId + tenantId: subscription().tenantId + azureADOnlyAuthentication: true + } + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + version: '12.0' + } + tags: { + 'aspire-resource-name': 'azuresql' + } +} + +resource sqlFirewallRule_AllowAllAzureIps 'Microsoft.Sql/servers/firewallRules@2024-05-01-preview' = { + name: 'AllowAllAzureIps' + properties: { + endIpAddress: '0.0.0.0' + startIpAddress: '0.0.0.0' + } + parent: azuresql +} + +resource database 'Microsoft.Sql/servers/databases@2024-05-01-preview' = { + name: 'database' + location: location + properties: { + freeLimitExhaustionBehavior: 'AutoPause' + useFreeLimit: true + } + sku: { + name: 'GP_S_Gen5_2' + } + parent: azuresql +} + +output sqlServerFqdn string = azuresql.properties.fullyQualifiedDomainName + +output name string = azuresql.name + +output sqlServerAdminName string = azuresql.properties.administrators.login +``` + +The preceding Bicep provisions an Azure SQL Server with a managed identity administrator, TLS 1.2 minimum, and a General Purpose Serverless database with the Azure free offer enabled. + +The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files. + +#### Customize provisioning infrastructure + +All Aspire Azure resources are subclasses of the `AzureProvisioningResource` type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources using the `ConfigureInfrastructure` API. For example, you can configure the `sku`, `version`, and more. The following example demonstrates how to customize the Azure SQL Database resource: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var sql = builder.AddAzureSqlServer("sql") + .ConfigureInfrastructure(infra => + { + var sqlServer = infra.GetProvisionableResources() + .OfType() + .Single(); + + sqlServer.MinTlsVersion = SqlMinimalTlsVersion.Tls1_3; + + var database = infra.GetProvisionableResources() + .OfType() + .Single(); + + database.Sku = new SqlSku { Name = "HS_Gen5_2" }; // Hyperscale + }) + .AddDatabase("db"); +``` + +The preceding code: + +- Chains a call to the `ConfigureInfrastructure` API: + - The `infra` parameter is an instance of the `AzureResourceInfrastructure` type. + - The provisionable resources are retrieved by calling `GetProvisionableResources`. + - The `SqlServer` is configured with TLS 1.3 minimum. + - The `SqlDatabase` is configured with a Hyperscale SKU instead of the default serverless. + +For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.Sql](https://learn.microsoft.com/dotnet/api/azure.provisioning.sql) API documentation. + ### Connection properties When you reference Azure SQL Server resources using `WithReference`, the following connection properties are made available to the consuming project: diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-blobs.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-blobs.mdx index b7c013f5..33c21ba7 100644 --- a/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-blobs.mdx +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-blobs.mdx @@ -204,6 +204,93 @@ To connect to the storage resource from Azure Storage Explorer, follow these ste You're free to explore the storage account and its contents using the Azure Storage Explorer. For more information on using the Azure Storage Explorer, see [Get started with Storage Explorer](https://learn.microsoft.com/azure/storage/storage-explorer/vs-azure-tools-storage-manage-with-storage-explorer). +### Provisioning-generated Bicep + +If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by-hand—the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure Storage resource with blob storage, the following Bicep is generated: + +```bicep title="Generated Bicep — storage.bicep" +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param principalId string + +param principalType string + +resource storage 'Microsoft.Storage/storageAccounts@2024-01-01' = { + name: take('storage${uniqueString(resourceGroup().id)}', 24) + kind: 'StorageV2' + location: location + sku: { + name: 'Standard_GRS' + } + properties: { + accessTier: 'Hot' + allowSharedKeyAccess: false + minimumTlsVersion: 'TLS1_2' + networkAcls: { + defaultAction: 'Allow' + } + } + tags: { + 'aspire-resource-name': 'storage' + } +} + +resource blobs 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = { + name: 'default' + parent: storage +} + +resource storage_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(storage.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: principalType + } + scope: storage +} + +output blobEndpoint string = storage.properties.primaryEndpoints.blob + +output name string = storage.name +``` + +The preceding Bicep provisions an Azure Storage account with a blob service, Standard GRS redundancy, TLS 1.2 minimum, and role assignments for blob data access. + +The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files. + +#### Customize provisioning infrastructure + +All Aspire Azure resources are subclasses of the `AzureProvisioningResource` type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources using the `ConfigureInfrastructure` API. For example, you can configure the `sku`, `accessTier`, and more. The following example demonstrates how to customize the Azure Storage resource: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var storage = builder.AddAzureStorage("storage") + .ConfigureInfrastructure(infra => + { + var storageAccount = infra.GetProvisionableResources() + .OfType() + .Single(); + + storageAccount.Sku = new StorageSku { Name = StorageSkuName.PremiumLrs }; + storageAccount.AccessTier = StorageAccountAccessTier.Premium; + storageAccount.Tags.Add("environment", "production"); + }); + +var blobs = storage.AddBlobs("blobs"); +``` + +The preceding code: + +- Chains a call to the `ConfigureInfrastructure` API: + - The `infra` parameter is an instance of the `AzureResourceInfrastructure` type. + - The provisionable resources are retrieved by calling `GetProvisionableResources`. + - The `StorageAccount` is configured with Premium LRS storage and a custom tag. + +For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.Storage](https://learn.microsoft.com/dotnet/api/azure.provisioning.storage) API documentation. + ### Connection properties When you reference Azure Storage resources using `WithReference`, the following connection properties are made available to the consuming project: diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-datalake.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-datalake.mdx new file mode 100644 index 00000000..760b1d26 --- /dev/null +++ b/src/frontend/src/content/docs/integrations/cloud/azure/azure-storage-datalake.mdx @@ -0,0 +1,278 @@ +--- +title: Azure Data Lake Storage +description: Learn how to use the Aspire Azure Data Lake Storage integration to connect to Azure Data Lake Storage Gen2 for big data analytics workloads. +--- + +import { Aside } from '@astrojs/starlight/components'; +import { Image } from 'astro:assets'; +import InstallPackage from '@components/InstallPackage.astro'; +import InstallDotNetPackage from '@components/InstallDotNetPackage.astro'; +import storageIcon from '@assets/icons/azure-storagecontainer-icon.png'; + +Azure Data Lake Storage logo + +[Azure Data Lake Storage Gen2](https://azure.microsoft.com/services/storage/data-lake-storage/) is a set of capabilities built on Azure Blob Storage for big data analytics. The Aspire Azure Data Lake Storage integration enables you to connect to Azure Data Lake Storage instances from your applications. + +## Hosting integration + +The Azure Data Lake Storage hosting integration models a Data Lake resource as a child of an Azure Storage resource. To add a Data Lake resource, install the [📦 Aspire.Hosting.Azure.Storage](https://www.nuget.org/packages/Aspire.Hosting.Azure.Storage) NuGet package in your [AppHost](/get-started/app-host/) project: + + + +### Add Azure Data Lake resource + +In your AppHost project, call `AddDataLake` on an `IResourceBuilder` to add a Data Lake resource: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var storage = builder.AddAzureStorage("storage"); +var dataLake = storage.AddDataLake("datalake"); + +builder.AddProject() + .WithReference(dataLake); + +builder.Build().Run(); +``` + +The preceding code: + +- Adds an Azure Storage resource named `storage`. +- Adds a Data Lake resource named `datalake` as a child of the storage resource. +- Passes a reference to the Data Lake resource to the `ExampleProject`. + + + +### Add Azure Data Lake file system resource + +You can also add a Data Lake file system resource directly from the storage resource: + +```csharp title="C# — AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var storage = builder.AddAzureStorage("storage"); +var dataLake = storage.AddDataLake("datalake"); +var fileSystem = storage.AddDataLakeFileSystem("analytics", "analytics-data"); + +builder.AddProject() + .WithReference(dataLake) + .WithReference(fileSystem); + +builder.Build().Run(); +``` + +The `AddDataLakeFileSystem` method takes: +- `name`: The resource name used in Aspire +- `dataLakeFileSystemName` (optional): The actual file system name in Azure. Defaults to the resource name if not specified. + + + +### Customize provisioning infrastructure + +The Data Lake resource is part of the Azure Storage resource, which is a subclass of `AzureProvisioningResource`. You can customize the generated Bicep using the `ConfigureInfrastructure` API on the storage resource. For example, you can configure the storage SKU, access tier, and other properties: + +```csharp title="AppHost.cs" +var builder = DistributedApplication.CreateBuilder(args); + +var storage = builder.AddAzureStorage("storage") + .ConfigureInfrastructure(infra => + { + var storageAccount = infra.GetProvisionableResources() + .OfType() + .Single(); + + storageAccount.Sku = new StorageSku { Name = StorageSkuName.PremiumLrs }; + storageAccount.Tags.Add("workload", "analytics"); + }); + +var dataLake = storage.AddDataLake("datalake"); + +builder.AddProject() + .WithReference(dataLake); +``` + +For more information on customizing Azure Storage provisioning, see [Azure Blob Storage: Customize provisioning infrastructure](/integrations/cloud/azure/azure-storage-blobs/#customize-provisioning-infrastructure). + +## Client integration + +To get started with the Aspire Azure Data Lake Storage client integration, install the [📦 Aspire.Azure.Storage.Files.DataLake](https://www.nuget.org/packages/Aspire.Azure.Storage.Files.DataLake) NuGet package in your client-consuming project: + + + +### Add Azure Data Lake service client + +In the `Program.cs` file of your client-consuming project, call `AddAzureDataLakeServiceClient` to register a `DataLakeServiceClient` for dependency injection: + +```csharp +builder.AddAzureDataLakeServiceClient("datalake"); +``` + +You can then retrieve the `DataLakeServiceClient` instance using dependency injection: + +```csharp +public class ExampleService(DataLakeServiceClient client) +{ + // Use client... +} +``` + +### Add Azure Data Lake file system client + +You can also register a `DataLakeFileSystemClient` for accessing a specific file system: + +```csharp +builder.AddAzureDataLakeFileSystemClient("analytics"); +``` + +You can then retrieve the `DataLakeFileSystemClient` instance using dependency injection: + +```csharp +public class ExampleService(DataLakeFileSystemClient client) +{ + // Use client... +} +``` + +### Keyed services + +Both client methods have keyed variants for registering multiple clients: + +```csharp +builder.AddKeyedAzureDataLakeServiceClient("datalake1"); +builder.AddKeyedAzureDataLakeServiceClient("datalake2"); + +builder.AddKeyedAzureDataLakeFileSystemClient("analytics"); +builder.AddKeyedAzureDataLakeFileSystemClient("archive"); +``` + +### Configuration + +The Azure Data Lake Storage client integration supports multiple configuration approaches. + +#### Use a connection string + +Provide the connection name when calling `AddAzureDataLakeServiceClient`: + +```csharp +builder.AddAzureDataLakeServiceClient("datalake"); +``` + +The connection string is retrieved from the `ConnectionStrings` section. Two formats are supported: + +**Service URI (recommended)**: + +```json +{ + "ConnectionStrings": { + "datalake": "https://{account_name}.dfs.core.windows.net/" + } +} +``` + +When using a service URI, the `DefaultAzureCredential` is used for authentication. + +**For file system clients**, include the file system name: + +```json +{ + "ConnectionStrings": { + "analytics": "https://{account_name}.dfs.core.windows.net/;FileSystemName=analytics-data" + } +} +``` + +**Azure Storage connection string**: + +```json +{ + "ConnectionStrings": { + "datalake": "DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey;EndpointSuffix=core.windows.net" + } +} +``` + +#### Use configuration providers + +The integration loads settings from the `Aspire:Azure:Storage:Files:DataLake` configuration section: + +```json +{ + "Aspire": { + "Azure": { + "Storage": { + "Files": { + "DataLake": { + "ServiceUri": "https://{account_name}.dfs.core.windows.net/", + "DisableHealthChecks": false, + "DisableTracing": false + } + } + } + } + } +} +``` + +#### Use inline delegates + +Configure settings programmatically: + +```csharp +builder.AddAzureDataLakeServiceClient( + "datalake", + settings => settings.DisableHealthChecks = true); +``` + +Configure client options: + +```csharp +builder.AddAzureDataLakeServiceClient( + "datalake", + configureClientBuilder: clientBuilder => + clientBuilder.ConfigureOptions( + options => options.Diagnostics.ApplicationId = "myapp")); +``` + +### Client integration health checks + +By default, the integration adds a health check that verifies connectivity to Azure Data Lake Storage. The health check: + +- Is enabled when `DisableHealthChecks` is `false` (the default) +- Integrates with the `/health` HTTP endpoint + +### Observability and telemetry + +#### Logging + +The integration uses these log categories: + +- `Azure.Core` +- `Azure.Identity` + +#### Tracing + +The integration emits OpenTelemetry tracing activities: + +- `Azure.Storage.Files.DataLake.DataLakeServiceClient` +- `Azure.Storage.Files.DataLake.DataLakeFileSystemClient` + +#### Metrics + +The Azure SDK for Data Lake Storage doesn't currently emit metrics. + +## See also + +- [Azure.Storage.Files.DataLake SDK documentation](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Files.DataLake/README.md) +- [Azure Data Lake Storage Gen2](https://learn.microsoft.com/azure/storage/blobs/data-lake-storage-introduction) +- [Azure Storage Blobs integration](/integrations/cloud/azure/azure-storage-blobs/) diff --git a/src/frontend/src/content/docs/integrations/cloud/azure/customize-resources.mdx b/src/frontend/src/content/docs/integrations/cloud/azure/customize-resources.mdx index a5b93b83..332207bc 100644 --- a/src/frontend/src/content/docs/integrations/cloud/azure/customize-resources.mdx +++ b/src/frontend/src/content/docs/integrations/cloud/azure/customize-resources.mdx @@ -9,7 +9,13 @@ When working with Azure integrations in Aspire, you often need to customize the ## Azure.Provisioning customization -The `ConfigureInfrastructure` API provides a strongly-typed, C#-based way to customize Azure resources. This is the recommended approach for most customizations. +The `ConfigureInfrastructure` API provides a strongly-typed, C#-based way to customize Azure resources. This is the recommended approach for most customizations. It uses the [Azure.Provisioning](https://www.nuget.org/packages/Azure.Provisioning) library to generate Bicep from C# code. + + ### Basic infrastructure customization diff --git a/src/frontend/src/content/docs/reference/cli/commands/aspire-ps.mdx b/src/frontend/src/content/docs/reference/cli/commands/aspire-ps.mdx new file mode 100644 index 00000000..1a15d024 --- /dev/null +++ b/src/frontend/src/content/docs/reference/cli/commands/aspire-ps.mdx @@ -0,0 +1,85 @@ +--- +title: aspire ps command +description: Learn about the aspire ps command which lists all running Aspire AppHost processes with their process IDs and dashboard URLs. +--- + +import Include from '@components/Include.astro'; + +## Name + +`aspire ps` - List running Aspire AppHosts. + +## Synopsis + +```bash title="Aspire CLI" +aspire ps [options] +``` + +## Description + +The `aspire ps` command lists all running Aspire AppHost processes. The output includes the AppHost project path, process IDs, and dashboard URLs for each running instance. + +The command scans for running AppHosts by checking the backchannel connections in the `~/.aspire/backchannels/` directory. This approach is fast because it doesn't need to recursively search for project files. + +The default output is a human-readable table with the following columns: + +| Column | Description | +|--------|-------------| +| `PATH` | The file path to the AppHost project | +| `PID` | The process ID of the running AppHost | +| `CLI_PID` | The process ID of the CLI that started the AppHost | +| `DASHBOARD` | The dashboard URL with login token | + +In-scope AppHosts (those within the current directory) are displayed first, followed by out-of-scope AppHosts. + +## Options + +The following options are available: + +- **`--json`** + + Output results in JSON format. This is useful for scripting and automation scenarios. The JSON output includes an array of AppHost objects with `appHostPath`, `appHostPid`, `cliPid`, and `dashboardUrl` properties. + +- + +- + +## Examples + +- List all running AppHosts in table format: + + ```bash title="Aspire CLI" + aspire ps + ``` + + Example output: + + ```text title="Output" + PATH PID CLI_PID DASHBOARD + ./src/MyApp.AppHost/MyApp.AppHost.csproj 12345 12340 https://localhost:17244/login?t=abc123 + /home/user/other/OtherApp.AppHost.csproj 67890 67885 https://localhost:17250/login?t=def456 + ``` + +- Output running AppHosts as JSON for scripting: + + ```bash title="Aspire CLI" + aspire ps --json + ``` + + Example output: + + ```json title="Output" + [ + { + "appHostPath": "./src/MyApp.AppHost/MyApp.AppHost.csproj", + "appHostPid": 12345, + "cliPid": 12340, + "dashboardUrl": "https://localhost:17244/login?t=abc123" + } + ] + ``` + +## See also + +- [aspire run](/reference/cli/commands/aspire-run/) +- [aspire stop](/reference/cli/commands/aspire-stop/) diff --git a/src/frontend/src/content/docs/reference/cli/commands/aspire-stop.mdx b/src/frontend/src/content/docs/reference/cli/commands/aspire-stop.mdx new file mode 100644 index 00000000..d0dd3384 --- /dev/null +++ b/src/frontend/src/content/docs/reference/cli/commands/aspire-stop.mdx @@ -0,0 +1,60 @@ +--- +title: aspire stop command +description: Learn about the aspire stop command which stops a running Aspire AppHost process. +--- + +import Include from '@components/Include.astro'; + +## Name + +`aspire stop` - Stop a running Aspire AppHost. + +## Synopsis + +```bash title="Aspire CLI" +aspire stop [options] +``` + +## Description + +The `aspire stop` command stops a running Aspire AppHost process. The CLI scans for running AppHosts and terminates the selected instance along with all its child processes. + +When executed without the `--project` option, the command: + +1. Scans for all running AppHost processes. +2. If multiple AppHosts are running within the current directory scope, prompts you to select which one to stop. +3. If only one AppHost is running in scope, stops it directly. +4. If no in-scope AppHosts are found but out-of-scope AppHosts exist, displays all running AppHosts for selection. + +The command sends a stop signal to the CLI process that started the AppHost, which ensures a clean shutdown of all resources including the dashboard and any containers or processes that were started. + +## Options + +The following options are available: + +- **`--project `** + + The path to the Aspire AppHost project file. When specified, the command stops only the AppHost running from that project file without prompting for selection. + +- + +- + +## Examples + +- Stop the AppHost running in the current directory scope: + + ```bash title="Aspire CLI" + aspire stop + ``` + +- Stop a specific AppHost project: + + ```bash title="Aspire CLI" + aspire stop --project './src/MyApp.AppHost/MyApp.AppHost.csproj' + ``` + +## See also + +- [aspire run](/reference/cli/commands/aspire-run/) +- [aspire ps](/reference/cli/commands/aspire-ps/) diff --git a/src/frontend/src/content/docs/reference/cli/commands/aspire.mdx b/src/frontend/src/content/docs/reference/cli/commands/aspire.mdx index 0ee19f16..fec9ab1f 100644 --- a/src/frontend/src/content/docs/reference/cli/commands/aspire.mdx +++ b/src/frontend/src/content/docs/reference/cli/commands/aspire.mdx @@ -51,8 +51,10 @@ The following commands are available: | [`aspire init`](../aspire-init/) | Stable | Initialize Aspire support in an existing solution or create a single-file AppHost. | | [`aspire mcp`](../aspire-mcp/) | Stable | Manage MCP (Model Context Protocol) server. | | [`aspire new`](../aspire-new/) | Stable | Create an Aspire sample project from a template. | +| [`aspire ps`](../aspire-ps/) | Stable | List running Aspire AppHost processes. | | [`aspire publish`](../aspire-publish/) | Preview | Generates deployment artifacts for an Aspire apphost project. | | [`aspire run`](../aspire-run/) | Stable | Run an Aspire apphost for local development. | +| [`aspire stop`](../aspire-stop/) | Stable | Stop a running Aspire AppHost. | | [`aspire update`](../aspire-update/) | Preview | Update Aspire packages and templates in your project. | ## Examples diff --git a/src/frontend/src/content/docs/reference/cli/configuration.mdx b/src/frontend/src/content/docs/reference/cli/configuration.mdx index 1d9ace4d..6edf3bbc 100644 --- a/src/frontend/src/content/docs/reference/cli/configuration.mdx +++ b/src/frontend/src/content/docs/reference/cli/configuration.mdx @@ -92,17 +92,17 @@ The following is an example with all supported settings: { "appHostPath": "../AspireShop/AspireShop.AppHost/AspireShop.AppHost.csproj", "features": { - "deployCommandEnabled": "true", - "doCommandEnabled": "true", + "defaultWatchEnabled": "true", + "dotnetSdkInstallationEnabled": "true", "execCommandEnabled": "true", "minimumSdkCheckEnabled": "true", "orphanDetectionWithTimestampEnabled": "true", "packageSearchDiskCachingEnabled": "true", - "publishCommandEnabled": "true", + "polyglotSupportEnabled": "true", + "runningInstanceDetectionEnabled": "true", + "showAllTemplates": "true", "showDeprecatedPackages": "true", - "singleFileAppHostEnabled": "true", "stagingChannelEnabled": "true", - "updateCommandEnabled": "true", "updateNotificationsEnabled": "true" } } diff --git a/src/frontend/src/content/docs/reference/cli/includes/config-settings-table.md b/src/frontend/src/content/docs/reference/cli/includes/config-settings-table.md index a8682327..a14d4391 100644 --- a/src/frontend/src/content/docs/reference/cli/includes/config-settings-table.md +++ b/src/frontend/src/content/docs/reference/cli/includes/config-settings-table.md @@ -11,6 +11,7 @@ title: Config Settings Table | `features.minimumSdkCheckEnabled` | Enforce minimum SDK version. | | `features.orphanDetectionWithTimestampEnabled` | Use timestamp-based orphan detection. | | `features.packageSearchDiskCachingEnabled` | Cache package search results on disk. | +| `features.polyglotSupportEnabled` | Enable polyglot (non-.NET) project support. | | `features.runningInstanceDetectionEnabled` | Enable detection of already running instances. | | `features.showAllTemplates` | Show all templates including experimental ones. | | `features.showDeprecatedPackages` | Show deprecated packages. | diff --git a/src/frontend/src/data/integration-docs.json b/src/frontend/src/data/integration-docs.json index 29ab9d20..85dc517a 100644 --- a/src/frontend/src/data/integration-docs.json +++ b/src/frontend/src/data/integration-docs.json @@ -43,6 +43,10 @@ "match": "Aspire.Azure.Storage.Blobs", "href": "/integrations/cloud/azure/azure-storage-blobs/" }, + { + "match": "Aspire.Azure.Storage.Files.DataLake", + "href": "/integrations/cloud/azure/azure-storage-datalake/" + }, { "match": "Aspire.Azure.Storage.Queues", "href": "/integrations/cloud/azure/azure-storage-queues/" @@ -55,10 +59,18 @@ "match": "Aspire.Elastic.Clients.Elasticsearch", "href": "/integrations/databases/elasticsearch/elasticsearch-get-started/" }, + { + "match": "Aspire.Hosting", + "href": "/architecture/overview/" + }, { "match": "Aspire.Hosting.AWS", "href": "https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/aspire-integrations.html" }, + { + "match": "Aspire.Hosting.Azure", + "href": "/integrations/cloud/azure/overview/" + }, { "match": "Aspire.Hosting.Azure.AIFoundry", "href": "/integrations/cloud/azure/azure-ai-foundry/" @@ -73,7 +85,7 @@ }, { "match": "Aspire.Hosting.Azure.ApplicationInsights", - "href": "/integrations/cloud/azure/overview/" + "href": "/integrations/cloud/azure/azure-application-insights/" }, { "match": "Aspire.Hosting.Azure.AppService", @@ -105,11 +117,11 @@ }, { "match": "Aspire.Hosting.Azure.Kusto", - "href": "/integrations/cloud/azure/overview/" + "href": "/integrations/cloud/azure/azure-data-explorer/" }, { "match": "Aspire.Hosting.Azure.OperationalInsights", - "href": "/integrations/cloud/azure/overview/" + "href": "/integrations/cloud/azure/azure-log-analytics/" }, { "match": "Aspire.Hosting.Azure.PostgreSQL", From 3e74d4c63302f42c053dae61131c3b5a523711c4 Mon Sep 17 00:00:00 2001 From: IEvangelist Date: Sun, 25 Jan 2026 12:19:43 -0600 Subject: [PATCH 04/31] Remove run_git_commands.ps1 script and associated documentation for CLI commands and diagnostics --- run_git_commands.ps1 | 58 -------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 run_git_commands.ps1 diff --git a/run_git_commands.ps1 b/run_git_commands.ps1 deleted file mode 100644 index 4dc0e4a7..00000000 --- a/run_git_commands.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -# Change to the repository directory -Set-Location "E:\GitHub\aspire.dev" - -# 1. Stage all changes -Write-Host "===== Step 1: git add -A =====" -git add -A -if ($LASTEXITCODE -eq 0) { - Write-Host "Successfully staged all changes" -} else { - Write-Host "Error staging changes (exit code: $LASTEXITCODE)" -} - -# 2. Check status -Write-Host "`n===== Step 2: git status =====" -git status - -# 3. Commit with the provided message -Write-Host "`n===== Step 3: git commit =====" -$commitMessage = @" -Documentation coverage audit: CLI commands, diagnostics, and config updates - -## New CLI command documentation -- aspire-stop.mdx: Stop a running Aspire AppHost -- aspire-ps.mdx: List running Aspire AppHost processes - -## New diagnostic pages (experimental APIs) -- ASPIREINTERACTION001: Interaction service experimental API -- ASPIREDOTNETTOOL: .NET tool resource experimental API -- ASPIREPOSTGRES001: PostgreSQL MCP experimental API -- ASPIREEXTENSION001: Extension debugging experimental API -- ASPIRECONTAINERSHELLEXECUTION001: Container shell execution experimental API - -## Configuration updates -- Added missing polyglotSupportEnabled feature flag to config-settings-table.md -- Fixed stale feature flags in example settings.json - -## Navigation updates -- Updated sidebar with aspire ps and aspire stop commands -- Updated aspire.mdx commands table with new commands - -Note: aspire doctor command is covered by PR #270 -"@ - -git commit -m $commitMessage -if ($LASTEXITCODE -eq 0) { - Write-Host "Successfully committed changes" -} else { - Write-Host "Error during commit (exit code: $LASTEXITCODE)" -} - -# 4. Push changes -Write-Host "`n===== Step 4: git push =====" -git push -if ($LASTEXITCODE -eq 0) { - Write-Host "Successfully pushed changes" -} else { - Write-Host "Error during push (exit code: $LASTEXITCODE)" -} From 527d2703b4b43daa3f0e34923e43f50f31685811 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 26 Jan 2026 09:22:04 -0600 Subject: [PATCH 05/31] Remove obsolete aspire-doctor documentation as it is covered by PR #270 --- .netlify/state.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .netlify/state.json diff --git a/.netlify/state.json b/.netlify/state.json new file mode 100644 index 00000000..a3049dc5 --- /dev/null +++ b/.netlify/state.json @@ -0,0 +1,3 @@ +{ + "siteId": "d69ecd1a-0e08-45b4-8572-0046462d26bc" +} \ No newline at end of file From 3567ae34aab00b26950291134d1cfbad6078c196 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 26 Jan 2026 09:30:51 -0600 Subject: [PATCH 06/31] Update VSCode settings for Prettier and enhance troubleshooting documentation --- .vscode/settings.json | 16 ++-- .../content/docs/get-started/first-app.mdx | 6 +- .../docs/get-started/troubleshooting.mdx | 74 +++++++++++-------- 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2114e504..119c31a4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,27 +1,27 @@ { "prettier.documentSelectors": ["**/*.astro"], "[astro]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[markdown]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[mdx]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascriptreact]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[yaml]": { - "editor.defaultFormatter": "prettier.prettier-vscode" + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/src/frontend/src/content/docs/get-started/first-app.mdx b/src/frontend/src/content/docs/get-started/first-app.mdx index 6c74716b..87b1d61e 100644 --- a/src/frontend/src/content/docs/get-started/first-app.mdx +++ b/src/frontend/src/content/docs/get-started/first-app.mdx @@ -375,6 +375,8 @@ import pythonDashboardDark from '@assets/get-started/python-aspire-dashboard-dar You might be eager to deploy this app next—and we'll show you how Aspire handles that—but you're probably also wondering: "How do I _test_ all this?" Great question! Aspire doesn't just orchestrate locally and deploy, it also helps you test service and resource integrations too. Ready to dive in? [Write your first test](/testing/write-your-first-test/) 💜 + -Having trouble? Check out our [Troubleshooting guide](/get-started/troubleshooting/) for solutions to common problems. - \ No newline at end of file +## See also + +- Having trouble? Check out our [Troubleshooting guide](/get-started/troubleshooting/) for solutions to common problems. \ No newline at end of file diff --git a/src/frontend/src/content/docs/get-started/troubleshooting.mdx b/src/frontend/src/content/docs/get-started/troubleshooting.mdx index db0a1127..dab4e27c 100644 --- a/src/frontend/src/content/docs/get-started/troubleshooting.mdx +++ b/src/frontend/src/content/docs/get-started/troubleshooting.mdx @@ -3,10 +3,10 @@ title: Aspire troubleshooting guide description: Solutions to common problems when getting started with .NET Aspire, including Docker issues, port conflicts, and service discovery errors. --- -import { Aside } from '@astrojs/starlight/components'; +import { Aside, Steps } from '@astrojs/starlight/components'; import { Kbd } from 'starlight-kbd/components'; import LearnMore from '@components/LearnMore.astro'; -import Expand from '@components/Expand.astro'; +import OsAwareTabs from '@components/OsAwareTabs.astro'; Having issues getting started with Aspire? This page covers solutions to the most common problems developers encounter. @@ -17,45 +17,57 @@ Having issues getting started with Aspire? This page covers solutions to the mos **Solution**: Start Docker Desktop (or Podman) before running `aspire run`. Check your system tray to confirm Docker is running with a green status indicator. ## Port already in use **Symptoms**: Error message like "Address already in use" or "Port 5000 is already bound." -**Solution**: +**Solution**: + - Stop any other applications using the same ports - If you have another Aspire app running, stop it first with - +### How to find processes using a port -**Windows:** -```powershell -netstat -ano | findstr :5000 -# Find the PID in the last column, then: -taskkill /PID /F -``` + +
+ + ```powershell + netstat -ano | findstr :5000 + # Find the PID in the last column, then: + taskkill /PID /F + ``` -**macOS/Linux:** -```bash -lsof -i :5000 -# Then kill the process: -kill -9 -``` +
+
- + ```bash + lsof -i :5000 + # Then kill the process: + kill -9 + ``` +
+ +
## Service shows "Unhealthy" in dashboard **Symptoms**: A service has a red status or shows "Unhealthy" in the Aspire dashboard. **Solution**: + + + 1. Click on the service name in the dashboard to view its logs 2. Look for startup errors or exceptions 3. Verify the health check endpoint exists (e.g., `/health` returns a 200 OK) 4. Check that all dependencies started successfully + + ## "Connection refused" errors **Symptoms**: Your frontend can't connect to the API, showing connection refused errors. @@ -74,7 +86,7 @@ builder.AddProject("frontend") **Solution**: Verify you're using `WithReference()` to connect resources. - +### Example: Connecting a database to your service In your AppHost: @@ -92,18 +104,17 @@ var connectionString = builder.Configuration.GetConnectionString("db"); The connection string is injected as an environment variable named `ConnectionStrings__db`. - - ## Container image pull failures **Symptoms**: Error messages about failing to pull container images, or "image not found" errors. **Solution**: + - Check your internet connection - Verify Docker Hub or your container registry is accessible - If using a corporate network, check proxy settings in Docker Desktop - +### How to manually pull and verify an image ```bash # Try pulling the image manually @@ -113,25 +124,28 @@ docker pull redis:latest # Settings > Resources > Proxies > Manual proxy configuration ``` - - ## Dashboard not loading **Symptoms**: The Aspire dashboard URL doesn't respond or shows a blank page. **Solution**: + + + 1. Check the console output for the correct dashboard URL (it may use a different port) 2. Ensure no browser extensions are blocking the page 3. Try a different browser or incognito mode 4. Check if antivirus or firewall is blocking the connection + + ## Service discovery not working **Symptoms**: Services can't find each other by name (e.g., `http://apiservice` doesn't resolve). **Solution**: Ensure you're using the service name exactly as defined in your AppHost. - +### Example: Correct service discovery setup ```csharp title="AppHost.cs" // In AppHost @@ -144,17 +158,13 @@ var client = new HttpClient { BaseAddress = new Uri("http://apiservice") }; ``` Also verify: + - Both services have `AddServiceDefaults()` called - The consuming service has `.WithReference(api)` in the AppHost - - - -For a deeper understanding of how service discovery works in Aspire, see [Service discovery](/fundamentals/service-discovery/). - - -For more help, see [Aspire Support](/support/) or check the [GitHub Discussions](https://github.com/dotnet/aspire/discussions). + For a deeper understanding of how service discovery works in Aspire, see + [Service discovery](/fundamentals/service-discovery/). ## See also From c080a4c6eb0840013bcca41a8b96dab5ef195ab1 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 26 Jan 2026 09:41:34 -0600 Subject: [PATCH 07/31] Add new diagnostic topic for container shell execution --- src/frontend/config/sidebar/diagnostic.topics.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/config/sidebar/diagnostic.topics.ts b/src/frontend/config/sidebar/diagnostic.topics.ts index 318fa2f5..87bb8c9b 100644 --- a/src/frontend/config/sidebar/diagnostic.topics.ts +++ b/src/frontend/config/sidebar/diagnostic.topics.ts @@ -47,6 +47,10 @@ export const diagnosticTopics: StarlightSidebarTopicsUserConfig = { label: 'ASPIRECONTAINERRUNTIME001', link: '/diagnostics/aspirecontainerruntime001', }, + { + label: 'ASPIRECONTAINERSHELLEXECUTION001', + link: '/diagnostics/aspirecontainershellexecution001', + }, { label: 'ASPIREDOCKERFILEBUILDER001', link: '/diagnostics/aspiredockerfilebuilder001', From e7502e140ec19012d3aa0d0722ff86251f805bdc Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 26 Jan 2026 09:53:27 -0600 Subject: [PATCH 08/31] Add new diagnostic topics for .NET tool and extensions --- src/frontend/config/sidebar/diagnostic.topics.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/frontend/config/sidebar/diagnostic.topics.ts b/src/frontend/config/sidebar/diagnostic.topics.ts index 87bb8c9b..96070a33 100644 --- a/src/frontend/config/sidebar/diagnostic.topics.ts +++ b/src/frontend/config/sidebar/diagnostic.topics.ts @@ -55,6 +55,14 @@ export const diagnosticTopics: StarlightSidebarTopicsUserConfig = { label: 'ASPIREDOCKERFILEBUILDER001', link: '/diagnostics/aspiredockerfilebuilder001', }, + { + label: 'ASPIREDOTNETTOOL', + link: '/diagnostics/aspiredotnettool', + }, + { + label: 'ASPIREEXTENSION001', + link: '/diagnostics/aspireextension001', + }, { label: 'ASPIREFILESYSTEM001', link: '/diagnostics/aspirefilesystem001', @@ -71,6 +79,10 @@ export const diagnosticTopics: StarlightSidebarTopicsUserConfig = { label: 'ASPIREPROBES001', link: '/diagnostics/aspireprobes001', }, + { + label: 'ASPIREPOSTGRES001', + link: '/diagnostics/aspirepostgres001', + }, { label: 'ASPIREUSERSECRETS001', link: '/diagnostics/aspireusersecrets001', From a6ee7723c1c8db19190783ad54f3fffb353bc29d Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 12:17:04 -0600 Subject: [PATCH 09/31] Update ASPIRE008 diagnostic page for GenerateAssemblyInfo requirement (#288) * Initial plan * Update ASPIRE008 diagnostic page for GenerateAssemblyInfo requirement Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> * Update project file samples to modern 13.x format Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --- .../content/docs/diagnostics/aspire008.mdx | 50 +++++++++---------- .../src/content/docs/diagnostics/overview.mdx | 2 +- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/frontend/src/content/docs/diagnostics/aspire008.mdx b/src/frontend/src/content/docs/diagnostics/aspire008.mdx index c6bc1f45..5c97a333 100644 --- a/src/frontend/src/content/docs/diagnostics/aspire008.mdx +++ b/src/frontend/src/content/docs/diagnostics/aspire008.mdx @@ -1,65 +1,63 @@ --- title: Compiler Error ASPIRE008 -description: Learn more about compiler Error ASPIRE008. The installed Aspire workload is deprecated. It is recommended to migrate to the new format. +description: Learn more about compiler Error ASPIRE008. The project requires GenerateAssemblyInfo to be enabled for the AppHost to function correctly. --- -import { Aside, Badge, Steps } from '@astrojs/starlight/components'; +import { Aside, Badge } from '@astrojs/starlight/components'; - + -> The Aspire workload that this project depends on is now deprecated. +> '[ProjectName]' project requires GenerateAssemblyInfo to be enabled. The Aspire AppHost relies on assembly metadata attributes to locate required dependencies. -This error appears when a project uses a version of Aspire that relies on the SDK workload, which is now deprecated. The error guides you to migrate your project to a supported version of Aspire that uses the SDK approach instead of the workload. +This error appears when an Aspire AppHost project has `GenerateAssemblyInfo` set to `false`. The Aspire AppHost relies on `AssemblyMetadataAttribute` to embed DCP (Developer Control Plane) and Dashboard paths during compilation. When `GenerateAssemblyInfo` is disabled, these attributes aren't generated, causing runtime failures about missing orchestration dependencies. ## Example -The following AppHost project file uses the deprecated Aspire workload: +The following AppHost project file has `GenerateAssemblyInfo` disabled, which causes `ASPIRE008`: ```xml title="C# project file" - + Exe - net8.0 + net10.0 enable enable - true 98048c9c-bf28-46ba-a98e-63767ee5e3a8 + false - - - - ``` -For more information on the Aspire SDK, see [Aspire SDK](https://learn.microsoft.com/dotnet/aspire/fundamentals/dotnet-aspire-sdk/). - ## To correct this error -Follow the migration guide at [https://aka.ms/aspire/update-to-sdk](https://aka.ms/aspire/update-to-sdk) to upgrade your project to a supported version of Aspire that uses the SDK approach. - -The migration typically involves: +Remove the `false` line from your project file, or set it to `true`: - +```xml title="C# project file" + -1. Updating your AppHost project file to use the `Aspire.AppHost.Sdk`. -1. Removing references to the deprecated workload. -1. Updating package references to supported versions. + + Exe + net10.0 + enable + enable + 98048c9c-bf28-46ba-a98e-63767ee5e3a8 + - + +``` ## Suppress the error -If you need to temporarily suppress this error, add the following property to your project file: +If you must temporarily suppress this error, add the following property to your project file: ```xml title="C# project file" - true + $(NoWarn);ASPIRE008 ``` diff --git a/src/frontend/src/content/docs/diagnostics/overview.mdx b/src/frontend/src/content/docs/diagnostics/overview.mdx index 834421f7..7be284bf 100644 --- a/src/frontend/src/content/docs/diagnostics/overview.mdx +++ b/src/frontend/src/content/docs/diagnostics/overview.mdx @@ -15,7 +15,7 @@ The following table lists the possible MSBuild and analyzer warnings and errors | [ASPIRE004](/diagnostics/aspire004/) | Warning | 'Project' is referenced by an Aspire Host project, but it is not an executable. | | [ASPIRE006](/diagnostics/aspire006/) | (Experimental) Error | Application model items must have valid names. | | [ASPIRE007](/diagnostics/aspire007/) | Error | 'Project' requires a reference to "Aspire.AppHost.Sdk" with version "9.0.0" or greater to work correctly. | -| [ASPIRE008](/diagnostics/aspire008/) | Error | The Aspire workload that this project depends on is now deprecated. | +| [ASPIRE008](/diagnostics/aspire008/) | Error | 'Project' requires GenerateAssemblyInfo to be enabled for the AppHost to function correctly. | | [ASPIREACADOMAINS001](/diagnostics/aspireacadomains001/) | (Experimental) Error | `ConfigureCustomDomain` is for evaluation purposes only and is subject to change or removal in future updates. | | [ASPIREAZURE001](/diagnostics/aspireazure001/) | (Experimental) Error | Publishers are for evaluation purposes only and are subject to change or removal in future updates. | | [ASPIREAZURE002](/diagnostics/aspireazure002/) | (Experimental) Error | Azure Container App Jobs are for evaluation purposes only and are subject to change or removal in future updates. | From a0443d36ccabc2cefbabdbbc2d71b7bc1b0926cb Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 26 Jan 2026 12:35:17 -0600 Subject: [PATCH 10/31] Add note about search functionality during local development in contributor guide --- src/frontend/config/sidebar/diagnostic.topics.ts | 1 + src/frontend/src/content/docs/community/contributor-guide.mdx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/frontend/config/sidebar/diagnostic.topics.ts b/src/frontend/config/sidebar/diagnostic.topics.ts index 96070a33..c82c8739 100644 --- a/src/frontend/config/sidebar/diagnostic.topics.ts +++ b/src/frontend/config/sidebar/diagnostic.topics.ts @@ -26,6 +26,7 @@ export const diagnosticTopics: StarlightSidebarTopicsUserConfig = { { label: 'Overview', link: '/diagnostics/overview' }, { label: 'Warnings', + collapsed: true, items: [ { label: 'ASPIRE001', link: '/diagnostics/aspire001' }, { label: 'ASPIRE002', link: '/diagnostics/aspire002' }, diff --git a/src/frontend/src/content/docs/community/contributor-guide.mdx b/src/frontend/src/content/docs/community/contributor-guide.mdx index 12b81465..29b866d0 100644 --- a/src/frontend/src/content/docs/community/contributor-guide.mdx +++ b/src/frontend/src/content/docs/community/contributor-guide.mdx @@ -118,6 +118,10 @@ Before you begin, ensure you have the following installed: Open your browser to `http://localhost:4321` (or the port shown in your terminal) + :::tip + During local development, the site search functionality is disabled. This is normal behavior as the search index is built during the production build process. To test search functionality, run a production build locally using `pnpm build` and then preview it with `pnpm preview`. + ::: + ### Known formatting limitations From 4d6f091f4be3952012fc89328057f446fed70d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Mon, 26 Jan 2026 13:13:33 -0800 Subject: [PATCH 11/31] =?UTF-8?q?Add=20polyglot=20AppHost=20documentation?= =?UTF-8?q?=20for=20TypeScript,=20Python,=20Go,=20Rust,=20=E2=80=A6=20(#27?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add polyglot AppHost documentation for TypeScript, Python, Go, Rust, and Java * feat: enhance PivotSelector component with marginTop prop for better layout control (#302) docs: update Polyglot AppHost documentation with new features and usage examples docs: add link to Polyglot AppHost in resource model documentation docs: include Polyglot AppHost reference in get-started guide for broader language support style: import utility classes for padding and margin in site CSS style: create utils.css for reusable padding and margin utility classes * fix: format project structure in polyglot AppHost documentation for clarity --------- Co-authored-by: David Pine --- src/frontend/config/sidebar/docs.topics.ts | 4 + .../src/components/PivotSelector.astro | 8 +- .../docs/app-host/polyglot-apphost.mdx | 835 ++++++++++++++++++ .../docs/architecture/resource-model.mdx | 4 + .../src/content/docs/get-started/app-host.mdx | 5 + src/frontend/src/styles/site.css | 3 +- src/frontend/src/styles/utils.css | 507 +++++++++++ 7 files changed, 1362 insertions(+), 4 deletions(-) create mode 100644 src/frontend/src/content/docs/app-host/polyglot-apphost.mdx create mode 100644 src/frontend/src/styles/utils.css diff --git a/src/frontend/config/sidebar/docs.topics.ts b/src/frontend/config/sidebar/docs.topics.ts index e81f9a62..250bb715 100644 --- a/src/frontend/config/sidebar/docs.topics.ts +++ b/src/frontend/config/sidebar/docs.topics.ts @@ -746,6 +746,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = { label: 'Configuration', slug: 'app-host/configuration', }, + { + label: 'Polyglot AppHost', + slug: 'app-host/polyglot-apphost', + }, { label: 'Certificate configuration', slug: 'app-host/certificate-configuration', diff --git a/src/frontend/src/components/PivotSelector.astro b/src/frontend/src/components/PivotSelector.astro index ce4836b6..e0af92c4 100644 --- a/src/frontend/src/components/PivotSelector.astro +++ b/src/frontend/src/components/PivotSelector.astro @@ -9,9 +9,10 @@ type Props = { options: Option[]; key: string; title?: string; + marginTop?: number | null; }; -const { options, key, title } = Astro.props; +const { options, key, title, marginTop } = Astro.props; ---
@@ -58,14 +59,14 @@ const { options, key, title } = Astro.props;
- diff --git a/src/frontend/src/components/FooterSocials.astro b/src/frontend/src/components/FooterSocials.astro new file mode 100644 index 00000000..383a5041 --- /dev/null +++ b/src/frontend/src/components/FooterSocials.astro @@ -0,0 +1,94 @@ +--- +import { Icon } from '@astrojs/starlight/components'; +import { socialConfig, type SocialLink } from '../../config/socials.config'; +--- + + + + diff --git a/src/frontend/src/components/TestimonialCarousel.astro b/src/frontend/src/components/TestimonialCarousel.astro index 5b1514b9..e2901cf8 100644 --- a/src/frontend/src/components/TestimonialCarousel.astro +++ b/src/frontend/src/components/TestimonialCarousel.astro @@ -97,7 +97,7 @@ function getColorForIndex(index: number): { bg: string; fg: string } { diff --git a/src/frontend/src/components/starlight/Footer.astro b/src/frontend/src/components/starlight/Footer.astro index 60cbab2d..44c18e35 100644 --- a/src/frontend/src/components/starlight/Footer.astro +++ b/src/frontend/src/components/starlight/Footer.astro @@ -3,6 +3,8 @@ import DefaultFooter from '@astrojs/starlight/components/Footer.astro'; import FooterLinks from '@components/FooterLinks.astro'; import FooterLegal from '@components/FooterLegal.astro'; import FooterResources from '@components/FooterResources.astro'; +import FooterPreferences from '@components/FooterPreferences.astro'; +import FooterSocials from '@components/FooterSocials.astro'; import AspireMap from '@components/AspireMap.astro'; import Expand from '@components/Expand.astro'; @@ -15,6 +17,9 @@ const showMap = isHomepage(Astro);