Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 1, 2025

Description

Adds support for configuring multiple storage accounts when using AddAzureStorage().RunAsEmulator(). Azurite supports custom accounts via the AZURITE_ACCOUNTS environment variable, enabling isolation between different parts of an application without running multiple emulator containers.

For more information on Azurite's custom storage accounts feature, see the Azurite documentation.

New public API:

  • WithAccounts(params string[] accountNames) - Configures custom accounts on the Azurite emulator (keys are auto-generated)
  • WithAccount(string accountName) - Assigns a specific account to blob, queue, or table resources

Usage:

var storage = builder.AddAzureStorage("storage")
    .RunAsEmulator(c => c.WithAccounts("legacy", "newdata"));

var legacyTables = storage.AddTables("legacy-tables").WithAccount("legacy");
var newTables = storage.AddTables("new-tables").WithAccount("newdata");

Fixes #8931

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?
Original prompt

This section details on the original issue you should resolve

<issue_title>Support for AddAzureStorage extensions with RunAsEmulator to have multiple accounts</issue_title>
<issue_description>### Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

I am trying to get Aspire up and running with Azure Storage. My requirement is to use Azure Tables, Blobs, and Queues under separate accounts without the need to run multiple Azurite containers.
Azurite itself supports multiple accounts as described at Azurite use-custom-storage-accounts-and-keys
However, with Aspire this does not seem possible..

Desire to have this is due to:

  • Resource pressure
    • The more containers running the more pressure on local machine,
    • CI has limited resources when tests are run.
  • Application requirements
    • One storage account is used for legacy tables (old structure).
    • Another storage account is used for new tables due to historical schema changes.
  • Isolation between containers
    • The application references another container that also requires Azure Tables.
    • Reusing the same connection would cause pollution of the storage and potential table conflicts.
  • Integration testing
    • Multiple accounts are used to keep test data separated and allow clean parallelization, for example
      • One account for persistence tests
      • Another for system tests
      • Additional accounts for other scenarios

Describe the solution you'd like

When using AddAzureStorage with RunAsEmulator to have the capability to define the account to use for AddTables, AddBlobs, AddQueues.

Additional context

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits December 1, 2025 09:30
Implements the ability to configure multiple custom storage accounts
for the Azure Storage emulator (Azurite) when using RunAsEmulator.

New public types:
- AzureStorageEmulatorAccount class for defining custom accounts

New extension methods:
- WithAccounts() on AzureStorageEmulatorResource to configure accounts
- WithAccount() on AzureBlobStorageResource to specify which account to use
- WithAccount() on AzureQueueStorageResource to specify which account to use
- WithAccount() on AzureTableStorageResource to specify which account to use

Usage example:
```csharp
var legacyAccount = new AzureStorageEmulatorAccount("legacy");
var newAccount = new AzureStorageEmulatorAccount("newdata");

var storage = builder.AddAzureStorage("storage")
    .RunAsEmulator(c => c.WithAccounts(legacyAccount, newAccount));

var legacyTables = storage.AddTables("legacy-tables").WithAccount(legacyAccount);
var newTables = storage.AddTables("new-tables").WithAccount(newAccount);
```

Co-authored-by: davidfowl <[email protected]>
Copilot AI changed the title [WIP] Support AddAzureStorage extensions for multiple accounts with RunAsEmulator Support for multiple Azurite storage accounts in RunAsEmulator Dec 1, 2025
Copilot AI requested a review from davidfowl December 1, 2025 09:41
Copilot finished work on behalf of davidfowl December 1, 2025 09:41
@davidfowl
Copy link
Member

@copilot The factory methods don't seem idiomatic but the layering seems good. The account creation is the only atypical API.

Replace static Create() factory methods with direct constructor usage,
making the API more idiomatic per code review feedback.

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 1, 2025

@copilot The factory methods don't seem idiomatic but the layering seems good. The account creation is the only atypical API.

Removed the redundant factory methods (Create(name) and Create(name, key)) in commit fb2fc94. The class now uses only the public constructor, which is more idiomatic.

Copilot finished work on behalf of davidfowl December 1, 2025 09:55
@davidfowl
Copy link
Member

@copilot Maybe we can use strings so that accounts aren't objects.

var storage = builder.AddAzureStorage("storage")
                   .RunAsEmulator(c => c.WithAccounts("legacy"));

var legacyTables = storage.AddTables("legacy-tables").WithAccount("legacy");

OR

var storage = builder.AddAzureStorage("storage")
                   .RunAsEmulator(c => c.WithAccounts("acc1", "somekey"));

var legacyTables = storage.AddTables("another").WithAccount("acc1");

Per reviewer feedback, changed the public API to use simple strings
for account names instead of AzureStorageEmulatorAccount objects.

The new API is cleaner:
- WithAccounts("acc1", "acc2") instead of WithAccounts(account1, account2)
- WithAccount("acc1") instead of WithAccount(account1)

The AzureStorageEmulatorAccount class is now internal and used only
for internal tracking of account details.

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 1, 2025

@copilot Maybe we can use strings so that accounts aren't objects.

Simplified the API to use strings in commit 989935f. The API now looks like:

var storage = builder.AddAzureStorage("storage")
    .RunAsEmulator(c => c.WithAccounts("legacy", "newdata"));

var legacyTables = storage.AddTables("legacy-tables").WithAccount("legacy");

The AzureStorageEmulatorAccount class is now internal - only used for tracking account details internally.

Copilot finished work on behalf of davidfowl December 1, 2025 10:22
@davidfowl
Copy link
Member

@copilot Update the readme to show this feature and link to the original docs for it in azureite

@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 2025

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 13266

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 13266"

Added a link to the official Azurite documentation for custom
storage accounts and keys in the README.

Co-authored-by: davidfowl <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 1, 2025

@copilot Update the readme to show this feature and link to the original docs for it in azureite

Added link to the Azurite documentation in commit f1c9d10.

Copilot finished work on behalf of davidfowl December 1, 2025 10:30
@davidfowl
Copy link
Member

@copilot We can remove teh hardcoded values from AzureStorageEmulatorConnectionString now that we have a general AzureStorageEmulatorAccount

Copy link
Contributor Author

Copilot AI commented Dec 1, 2025

@copilot We can remove teh hardcoded values from AzureStorageEmulatorConnectionString now that we have a general AzureStorageEmulatorAccount

Unfortunately, this duplication is necessary due to the test infrastructure. The test project compiles AzureStorageEmulatorConnectionString.cs directly via a Compile Include, which means it doesn't have access to the AzureStorageEmulatorAccount class. I've added a comment explaining the constraint. If you'd like, I could explore removing the Compile Include from the test project instead, which would allow us to use the shared constants.

@sebastienros
Copy link
Member

Have you thought about using a singleton emulator and only accepting a single account in RunAsEmulator(x => x.WithAccount('foo')) such that the returned resource as the account and doesn't need to be repeated in tables,blobs, ...

When it runs it can aggregates all the account values for set the config.

var storageA = builder.AddAzureStorage("storage").RunAsEmulator(c => c.WithAccount("A"));
var storageB = builder.AddAzureStorage("storage").RunAsEmulator(c => c.WithAccount("B"));


var legacyTables = storageA.AddTables("legacy-tables");
var newTables = storageB.AddTables("legacy-tables");

How does that work though when you deployed on real storage? There won't be isolation anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants