Condividi tramite


Esercitazione: Collegare un'applicazione ASP.NET Core alle integrazioni di archiviazione .NET Aspire

Le applicazioni native per il cloud spesso richiedono soluzioni di archiviazione scalabili che offrono funzionalità come l'archiviazione blob, le code o i database NoSQL semistrutturati. .NET Aspire le integrazioni semplificano le connessioni con vari servizi di archiviazione, come ad esempio Azure Blob Storage. In questa esercitazione si creerà un'app ASP.NET Core che utilizza le integrazioni di .NET Aspire per connettersi a Azure Blob Storage e Azure Archiviazione di code per inviare segnalazioni di supporto. L'app invia i ticket a una coda per l'elaborazione e carica un file allegato nell'archivio. Si apprenderà come:

  • Creare un'app di base .NET configurata per utilizzare le integrazioni .NET Aspire
  • Aggiungere integrazioni .NET.NET Aspire per connettersi a più servizi di archiviazione
  • Configurare e usare le funzionalità del componente .NET.NET Aspire per inviare e ricevere dati

Prerequisiti

Per usare .NET.NET Aspire, è necessario che il codice seguente sia installato in locale:

Per altre informazioni, vedere .NET.NET Aspire configurazione e strumentie .NET.NET Aspire SDK.

Esplorare l'app di esempio completata

Una versione completa dell'app di esempio di questa esercitazione è disponibile su GitHub. Il progetto è strutturato anche come modello per l'Azure Developer CLI, ovvero è possibile usare il comando azd up per automatizzare il provisioning delle risorse Azure se è installato lo strumento .

git clone https://github.com/Azure-Samples/dotnet-aspire-connect-storage.git

Configurare le risorse di archiviazione Azure

Per questo articolo, è necessario l'accesso come collaboratore dei dati a un account di archiviazione Azure con un contenitore blob e una coda di archiviazione. Assicurarsi di disporre delle risorse e delle configurazioni seguenti:

Per questo articolo, dovrai creare un contenitore blob e una coda di archiviazione nell'ambiente di sviluppo locale utilizzando un emulatore. A tale scopo, utilizzare Azurite. Azurite è un Azure (emulatore server) compatibile con Azure (emulatore) gratuito e open source multipiattaforma .

Per utilizzare l'emulatore, è necessario installare Azurite.

  1. Un account di archiviazione Azure - Creare un account di archiviazione.
  2. Un contenitore di archiviazione BLOB denominato fileuploads - Creare un contenitore di archiviazione BLOB.
  3. Una coda di archiviazione denominata ticket - . Creare una coda di archiviazione.

Eseguire i comandi seguenti nell'interfaccia della riga di comando di Azure o in CloudShell per configurare le risorse di archiviazione necessarie Azure:

az group create --name aspirestorage --location eastus2
az storage account create -n aspirestorage -g aspirestorage -l eastus2
az storage container create -n fileuploads --account-name aspirestorage
az storage queue create -n tickets --account-name aspirestorage

È anche necessario assegnare i ruoli seguenti all'account utente con cui si è connessi Visual Studio:

Il Azure Developer CLI consente di gestire il provisioning e distribuire risorse Azure utilizzando un sistema a modelli. Questa esercitazione fornisce un modello completo che effettua il provisioning delle risorse Azure necessarie e include il codice dell'applicazione di esempio completato. Eseguire i comandi seguenti per inizializzare ed eseguire il modello:

  1. Eseguire azd auth login per accedere a Azure:

    azd auth login
    
  2. Eseguire azd init per clonare e inizializzare il modello di esempio:

    azd init --template dotnet-aspire-connect-storage
    
  3. Eseguire azd up per configurare le risorse di Azure.

    azd up
    
  4. Quando richiesto, selezionare la sottoscrizione e l'area Azure per le risorse fornite. Il modello viene eseguito e completa per te le seguenti attività:

    • Crea un account di archiviazione Azure con i servizi blob (archiviazione di grandi oggetti binari) e di accodamento abilitati.
    • Crea un contenitore di archiviazione BLOB denominato fileUploads
    • Crea una coda denominata tickets
    • Assegna i ruoli seguenti all'account utente che ha eseguito il modello.
      • Collaboratore ai dati dei BLOB di archiviazione
      • Collaboratore ai dati della coda di archiviazione

Al termine dell'operazione, sono disponibili due opzioni in futuro:

  • Opzione 1: eseguire l'app di esempio .NET nel modello src directory per sperimentare l'app completata.
  • Opzione 2: Costruire l'app di esempio passo dopo passo utilizzando le sezioni successive e connetterla alle risorse Azure fornite da azd.

Creare la soluzione di esempio

Creare un progetto .NET Aspire utilizzando o Visual Studio o il CLI di .NET.

  1. Nella parte superiore di Visual Studiopassare a File>Nuovo progetto>.
  2. Nella finestra di dialogo, cercare Aspire e selezionare .NET.NET Aspire l'applicazione Starter. Scegliere Avanti.
  3. Nella schermata Configura il nuovo progetto:
    • Inserire un Nome Soluzione di AspireStorage e selezionare Avanti.
  4. Nella schermata Informazioni aggiuntive:
    • Deselezionare Usare Redis per la memorizzazione nella cache (non obbligatorio per questa esercitazione).
    • Selezionare Crea.

Visual Studio crea una nuova soluzione di ASP.NET Core strutturata per utilizzare .NET Aspire.

La soluzione è costituita dai progetti seguenti:

  • AspireStorage.ApiService - progetto API con configurazioni predefinite del servizio .NET.NET Aspire.
  • AspireStorage.AppHost - Un progetto di orchestrazione progettato per connettere e configurare i diversi progetti e servizi della tua app. L'orchestratore deve essere impostato come progetto di avvio.
  • AspireStorage.ServiceDefaults: Una libreria di classi condivisa che contiene codice riutilizzabile tra i progetti nella tua soluzione.
  • AspireStorage.Web: un progetto di BlazorServer che funge da front-end della tua app.

Aggiungere il progetto Worker Service

Aggiungere quindi un progetto Worker Service alla soluzione per recuperare ed elaborare i messaggi man mano che vengono aggiunti alla coda di archiviazione Azure.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo della soluzione livello superiore aspirareStorage e selezionare AggiungiNuovo progetto.
  2. Cerca e seleziona il modello Worker Service e scegli Avanti.
  3. Per Nome progettoimmettere Aspiratore.WorkerService e selezionare Avanti.
  4. Nella schermata Informazioni aggiuntive:
    • Assicurarsi che sia selezionato .NET 9.0.
    • Assicurarsi che sia selezionato Enlist in .NET.NET Aspire orchestrazione e selezionare Crea.

Visual Studio aggiunge il progetto alla soluzione e aggiorna il file Program.cs del progetto AspireStorage.AppHost con una nuova riga di codice:

builder.AddProject<Projects.AspireStorage_WorkerService>(
    "aspirestorage-workerservice");

L'utensile Visual Studio ha aggiunto questa riga di codice per registrare il tuo nuovo progetto con l'oggetto IDistributedApplicationBuilder, che abilita le funzionalità di orchestrazione. Per altre informazioni, vedere panoramica dell'orchestrazione .NET.NET Aspire.

La struttura della soluzione completata dovrebbe essere simile alla seguente:

Una schermata che mostra la struttura della soluzione di archiviazione di esempio .NET.NET Aspire.

Aggiungere le integrazioni .NET Aspire all'app Blazor

Aggiungere il di integrazione e pacchetti di integrazione dell'archiviazione code al progetto AspireStorage.Web:Add the integration and Queue Storage integration packages to your AspireStorage.Web project:

dotnet add package Aspire.Azure.Storage.Blobs
dotnet add package Aspire.Azure.Storage.Queues

Il progetto AspireStorage.Web è ora configurato per l'uso delle integrazioni .NET.NET Aspire. Di seguito è riportato il file aggiornato AspireStorage.Web.csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\AspireStorage.ServiceDefaults\AspireStorage.ServiceDefaults.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Azure.Storage.Blobs" Version="9.0.0" />
    <PackageReference Include="Aspire.Azure.Storage.Queues" Version="9.0.0" />
  </ItemGroup>

</Project>

Il passaggio successivo consiste nell'aggiungere le integrazioni all'app.

Nel file Program.cs del progetto AspireStorage.Web aggiungere chiamate ai metodi di estensione AddAzureBlobClient e AddAzureQueueClient dopo la creazione del builder, ma prima della chiamata a AddServiceDefaults. Per altre informazioni, vedere impostazioni predefinite del servizio .NET.NET Aspire. Specificare il nome della stringa di connessione come parametro.

using AspireStorage.Web;
using AspireStorage.Web.Components;

using Azure.Storage.Blobs;
using Azure.Storage.Queues;

var builder = WebApplication.CreateBuilder(args);

builder.AddAzureBlobClient("BlobConnection");
builder.AddAzureQueueClient("QueueConnection");

// Add service defaults & Aspire components.
builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddOutputCache();

builder.Services.AddHttpClient<WeatherApiClient>(client =>
    {
        // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
        // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
        client.BaseAddress = new("https+http://apiservice");
    });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
else
{
    // In development, create the blob container and queue if they don't exist.
    var blobService = app.Services.GetRequiredService<BlobServiceClient>();
    var docsContainer = blobService.GetBlobContainerClient("fileuploads");

    await docsContainer.CreateIfNotExistsAsync();

    var queueService = app.Services.GetRequiredService<QueueServiceClient>();
    var queueClient = queueService.GetQueueClient("tickets");

    await queueClient.CreateIfNotExistsAsync();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.UseOutputCache();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapDefaultEndpoints();

app.Run();
using AspireStorage.Web;
using AspireStorage.Web.Components;

using Azure.Storage.Blobs;
using Azure.Storage.Queues;

var builder = WebApplication.CreateBuilder(args);

builder.AddAzureBlobClient("BlobConnection");
builder.AddAzureQueueClient("QueueConnection");

// Add service defaults & Aspire components.
builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddOutputCache();

builder.Services.AddHttpClient<WeatherApiClient>(client =>
    {
        // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
        // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
        client.BaseAddress = new("https+http://apiservice");
    });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.UseOutputCache();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapDefaultEndpoints();

app.Run();

Con le istruzioni aggiuntive using, questi metodi eseguono le attività seguenti:

All'avvio del progetto AspireStorage.Web, verrà creato un contenitore fileuploads in Azurite Blob Storage e una coda tickets in Azurite Queue Storage. Questa operazione è condizionale quando l'app è in esecuzione in un ambiente di sviluppo. Quando l'app è in esecuzione in un ambiente di produzione, si presuppone che il contenitore e la coda siano già stati creati.

Aggiungere l'integrazione .NET Aspire al Worker Service

Il servizio worker gestisce l'estrazione dei messaggi dalla coda Azure Storage per l'elaborazione. Aggiungi il pacchetto di integrazione per l'archiviazione della coda .NET AspireAzure alla tua app AspireStorage.WorkerService.

dotnet add package Aspire.Azure.Storage.Queues

Nel file Program.cs del progetto AspireStorage.WorkerService, aggiungere una chiamata al metodo di estensione AddAzureQueueClient dopo la creazione del builder, ma prima della chiamata a AddServiceDefaults:

using AspireStorage.WorkerService;

var builder = Host.CreateApplicationBuilder(args);

builder.AddAzureQueueClient("QueueConnection");

builder.AddServiceDefaults();
builder.Services.AddHostedService<WorkerService>();

var host = builder.Build();
host.Run();

Questo metodo gestisce le attività seguenti:

  • Registrare un QueueServiceClient con il contenitore DI per la connessione a Azure Storage Queues.
  • Abilitare automaticamente i controlli di integrità, la registrazione e i dati di telemetria corrispondenti per i rispettivi servizi.

Creare il modulo

L'app richiede un modulo per consentire all'utente di inviare informazioni sul ticket di supporto e caricare un allegato. L'app carica il file allegato nella proprietà Document (IFormFile) per Azure Blob Storage usando il BlobServiceClientinserito. Il QueueServiceClient invia un messaggio composto da Title e Description alla coda di archiviazione Azure.

Usare il markup Razor seguente per creare un modulo di base, sostituendo il contenuto del file Home.razor nella directory AspireStorage.Web/Components/Pages:

@page "/"

@using System.ComponentModel.DataAnnotations
@using Azure.Storage.Blobs
@using Azure.Storage.Queues

@inject BlobServiceClient BlobClient
@inject QueueServiceClient QueueServiceClient

<PageTitle>Home</PageTitle>

<div class="text-center">
    <h1 class="display-4">Request Support</h1>
</div>

<EditForm Model="@Ticket" FormName="Tickets" method="post"
          OnValidSubmit="@HandleValidSubmit" enctype="multipart/form-data">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="mb-4">
        <label>Issue Title</label>
        <InputText class="form-control" @bind-Value="@Ticket.Title" />
        <ValidationMessage For="() => Ticket.Title" />
    </div>
    <div class="mb-4">
        <label>Issue Description</label>
        <InputText class="form-control" @bind-Value="@Ticket.Description" />
        <ValidationMessage For="() => Ticket.Description" />
    </div>
    <div class="mb-4">
        <label>Attachment</label>
        <InputFile class="form-control" name="Ticket.Document" />
        <ValidationMessage For="() => Ticket.Document" />
    </div>
    <button class="btn btn-primary" type="submit">Submit</button>
    <button class="btn btn-danger mx-2" type="reset" @onclick=@ClearForm>Clear</button>
</EditForm>

@code {
    [SupplyParameterFromForm(FormName = "Tickets")]
    private SupportTicket Ticket { get; set; } = new();

    private async Task HandleValidSubmit()
    {
        var docsContainer = BlobClient.GetBlobContainerClient("fileuploads");

        // Upload file to blob storage
        await docsContainer.UploadBlobAsync(
            Ticket.Document.FileName,
            Ticket.Document.OpenReadStream());

        // Send message to queue
        var queueClient = QueueServiceClient.GetQueueClient("tickets");

        await queueClient.SendMessageAsync(
             $"{Ticket.Title} - {Ticket.Description}");

        ClearForm();
    }

    private void ClearForm() => Ticket = new();

    private class SupportTicket()
    {
        [Required] public string Title { get; set; } = default!;
        [Required] public string Description { get; set; } = default!;
        [Required] public IFormFile Document { get; set; } = default!;
    }
}

Per altre informazioni sulla creazione di moduli in Blazor, vedere panoramica dei moduli ASP.NET CoreBlazor.

Aggiornare l'AppHost

Il progetto AspireStorage.AppHost è l'orchestratore per la tua app. È responsabile della connessione e della configurazione dei diversi progetti e servizi dell'app. L'orchestratore deve essere impostato come progetto di avvio.

Per aggiungere il supporto dell'hosting di storage Azure al IDistributedApplicationBuilder, installare il pacchetto NuGet 📦Aspire.Hosting.Azure.Storage.

dotnet add package Aspire.Hosting.Azure.Storage

Sostituire il contenuto del file Program.cs nel progetto AspireStorage.AppHost con il codice seguente:

using Microsoft.Extensions.Hosting;

var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.AddAzureStorage("Storage");

if (builder.Environment.IsDevelopment())
{
    storage.RunAsEmulator();
}

var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");

var apiService = builder.AddProject<Projects.AspireStorage_ApiService>("apiservice");

builder.AddProject<Projects.AspireStorage_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService)
    .WithReference(blobs)
    .WithReference(queues); 

builder.AddProject<Projects.AspireStorage_WorkerService>("aspirestorage-workerservice")
    .WithReference(queues);

builder.Build().Run();

Il codice precedente aggiunge lo storage Azure, i blob e le code e, quando in modalità di sviluppo, usa l'emulatore. Ogni progetto definisce i riferimenti per queste risorse da cui dipendono.

using Microsoft.Extensions.Hosting;

var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.AddAzureStorage("Storage");

var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");

var apiService = builder.AddProject<Projects.AspireStorage_ApiService>("apiservice");

builder.AddProject<Projects.AspireStorage_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService)
    .WithReference(blobs)
    .WithReference(queues); 

builder.AddProject<Projects.AspireStorage_WorkerService>("aspirestorage-workerservice")
    .WithReference(queues);

builder.Build().Run();

Il codice precedente aggiunge l'archiviazione Azure, BLOB e code e definisce i riferimenti per queste risorse all'interno di ogni progetto che ne dipende.

Elaborare gli elementi nella coda

Quando viene inserito un nuovo messaggio nella coda tickets, il servizio di elaborazione deve recuperare, elaborare ed eliminare il messaggio. Aggiornare la classe Worker.cs sostituendo il contenuto con il codice seguente:

using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;

namespace AspireStorage.WorkerService;

public sealed class WorkerService(
    QueueServiceClient client,
    ILogger<WorkerService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var queueClient = client.GetQueueClient("tickets");
        await queueClient.CreateIfNotExistsAsync(cancellationToken: stoppingToken);

        while (!stoppingToken.IsCancellationRequested)
        {
            QueueMessage[] messages =
                await queueClient.ReceiveMessagesAsync(
                    maxMessages: 25, cancellationToken: stoppingToken);

            foreach (var message in messages)
            {
                logger.LogInformation(
                    "Message from queue: {Message}", message.MessageText);

                await queueClient.DeleteMessageAsync(
                    message.MessageId,
                    message.PopReceipt,
                    cancellationToken: stoppingToken);
            }

            // TODO: Determine an appropriate time to wait 
            // before checking for more messages.
            await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
        }
    }
}

Prima che il servizio di lavoro possa elaborare i messaggi, deve essere in grado di connettersi alla coda di archiviazione Azure. Con Azurite, è necessario assicurarsi che la coda sia disponibile prima che il servizio worker inizi ad eseguire l'elaborazione della coda dei messaggi.

using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;

namespace AspireStorage.WorkerService;

public sealed class WorkerService(
    QueueServiceClient client,
    ILogger<WorkerService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var queueClient = client.GetQueueClient("tickets");
        while (!stoppingToken.IsCancellationRequested)
        {
            QueueMessage[] messages =
                await queueClient.ReceiveMessagesAsync(
                    maxMessages: 25, cancellationToken: stoppingToken);

            foreach (var message in messages)
            {
                logger.LogInformation(
                    "Message from queue: {Message}", message.MessageText);

                await queueClient.DeleteMessageAsync(
                    message.MessageId,
                    message.PopReceipt,
                    cancellationToken: stoppingToken);
            }

            // TODO: Determine an appropriate time to wait 
            // before checking for more messages.
            await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
        }
    }
}

Il servizio di lavoro in background elabora i messaggi connettendosi alla coda di archiviazione Azure e prelevando i messaggi dalla coda.

Il servizio di lavoro elabora i messaggi nella coda e li elimina quando sono stati elaborati.

Configurare le stringhe di connessione

I progetti AspireStorage e AspireStorage.Worker devono essere configurati per connettersi all'account di archiviazione Azure corretto creato in precedenza. È possibile specificare gli endpoint per i servizi di blob e di accodamento nell'account di archiviazione usando il file appsettings.json in ogni progetto.

  1. Nel progetto AspireStorage aggiungere la configurazione seguente al file appsettings.Development.json:

      "ConnectionStrings": {
        "BlobConnection": "https://<your-storage-account-name>.blob.core.windows.net/",
        "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/"
      }
    
  2. Nel progetto AspireStorage.Worker, aggiungere la seguente configurazione al file appsettings.Development.json:

      "ConnectionStrings": {
        "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/"
      }
    

Eseguire e testare l'app in locale

L'app di esempio è ora pronta per il test. Verificare che i dati del modulo vengano inviati a Azure Blob Storage e Azure Archiviazione delle Code completando i seguenti passaggi:

  1. Premere il pulsante Esegui nella parte superiore di Visual Studio per avviare il dashboard del progetto .NET Aspire nel browser.

  2. Nella riga aspirastorage.web della pagina delle risorse, fai clic sul collegamento nella colonna Endpoint per aprire l'interfaccia utente dell'app.

    Una schermata che mostra la home page dell'applicazione di supporto .NET.NET Aspire.

  3. Immettere i dati di esempio nei campi del modulo Title e Description e selezionare un file semplice da caricare.

  4. Selezionare il pulsante Invia e il modulo invia il ticket di supporto per l'elaborazione e svuota il modulo.

  5. In una scheda del browser separata, utilizza il portale di Azure per navigare verso lo Storage browser nel tuo Storage Account Azure.

  6. Selezionare Contenitori e quindi navigare nel contenitore Documenti per visualizzare il file caricato.

  7. È possibile verificare che il messaggio nella coda sia stato elaborato esaminando i log di Project del dashboard .NET.NET Aspiree selezionando il aspirestorage.workerservice dall'elenco a discesa.

    Screenshot che mostra l'output della console dell'app Worker.

Sommario

L'app di esempio che hai creato dimostra il salvataggio permanente dei blob da un'app Web ASP.NET CoreBlazor e l'elaborazione delle code in un .NET Worker Service. L'app si connette all'archiviazione Azure utilizzando le integrazioni .NET Aspire. L'app invia i ticket di supporto a una coda per l'elaborazione e carica un allegato nello spazio di archiviazione.

Poiché scegli di usare Azurite, non è necessario pulire queste risorse al termine del test, poiché le hai create localmente nel contesto di un emulatore. L'emulatore ha consentito di testare l'app in locale senza incorrere in alcun costo, perché non è stato effettuato il provisioning o la creazione di risorse Azure.

Pulire le risorse

Esegui il seguente comando CLI Azure per eliminare il gruppo di risorse quando non hai più bisogno delle risorse Azure che hai creato. L'eliminazione del gruppo di risorse comporta anche l'eliminazione delle risorse contenute all'interno di essa.

az group delete --name <your-resource-group-name>

Per ulteriori informazioni, consultare Pulizia delle risorse in Azure.