microsoft/aspire · error · InvalidOperationException

The subnet ' ' already has an NSG created via shorthand…

Error message

The subnet '{builder.Resource.Name}' already has an NSG created via shorthand methods. Calling WithNetworkSecurityGroup would replace the existing NSG and discard those rules. Use either shorthand methods (AllowInbound, DenyInbound, etc.) or an explicit NSG, not both.

What it means

AzureSubnetResource can hold exactly one NSG. If shorthand rule methods (AllowInbound, DenyInbound, etc.) were used, the resource carries an implicitly created NSG; calling WithNetworkSecurityGroup with an explicit NSG builder would overwrite it and silently discard all rules added via shorthand. The method detects IsImplicitlyCreated and throws InvalidOperationException to force choosing one approach.

Solutions

  1. Remove the WithNetworkSecurityGroup call and keep using shorthand methods (AllowInbound/DenyInbound/AllowOutbound/DenyOutbound).
  2. Alternatively remove all shorthand rule calls and define the NSG fully via WithNetworkSecurityGroup.
  3. If you truly want to replace, first reset the subnet's NetworkSecurityGroup (clear the implicitly created NSG) before attaching the explicit one.
  4. Audit the subnet resource's NetworkSecurityGroup property to confirm which style is in use before adding rules.

Example fix

// before
subnet.AllowInbound("allow-http", 80);
subnet.WithNetworkSecurityGroup(nsg); // throws

// after
subnet.WithNetworkSecurityGroup(nsg); // explicit NSG defines all rules
Defensive patterns

Strategy: validation

Validate before calling

if (subnet.Resource.NetworkSecurityGroup is { IsImplicitlyCreated: true })
{
    // shorthand rules exist; do not call WithNetworkSecurityGroup
}

Try / catch

try { subnet.WithNetworkSecurityGroup(nsg); }
catch (InvalidOperationException ex) when (ex.Message.Contains("shorthand methods")) { /* pick one style: keep shorthand rules or drop them and use the explicit NSG */ }

Prevention

When it happens

Trigger: Calling subnet.WithNetworkSecurityGroup(nsg) after any shorthand call such as subnet.AllowInbound(...)/DenyInbound(...) on the same subnet resource.

Common situations: Mixing a documented explicit-NSG snippet with existing shorthand rule code; moving from quick shorthand rules to a full NSG definition during refactoring; two team members contributing rules in different styles to the same subnet.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/7d6e4fa7250d49e1. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Azure.Network/AzureVirtualNetworkExtensions.cs:471

    ///     .WithNetworkSecurityGroup(nsg);
    /// </code>
    /// </example>
    /// <exception cref="InvalidOperationException">
    /// Thrown when the subnet already has security rules added via shorthand methods
    /// (<see cref="AllowInbound"/>, <see cref="DenyInbound"/>, <see cref="AllowOutbound"/>, <see cref="DenyOutbound"/>).
    /// Use either shorthand methods or an explicit NSG, not both.
    /// </exception>
    [AspireExport]
    public static IResourceBuilder<AzureSubnetResource> WithNetworkSecurityGroup(
        this IResourceBuilder<AzureSubnetResource> builder,
        IResourceBuilder<AzureNetworkSecurityGroupResource> nsg)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(nsg);

        if (builder.Resource.NetworkSecurityGroup is { IsImplicitlyCreated: true })
        {
            throw new InvalidOperationException(
                $"The subnet '{builder.Resource.Name}' already has an NSG created via shorthand methods. " +
                $"Calling WithNetworkSecurityGroup would replace the existing NSG and discard those rules. " +
                $"Use either shorthand methods (AllowInbound, DenyInbound, etc.) or an explicit NSG, not both.");
        }

        builder.Resource.NetworkSecurityGroup = nsg.Resource;
        return builder;
    }

    /// <summary>
    /// Adds an inbound allow rule to the subnet's Network Security Group.
    /// </summary>
    /// <param name="builder">The subnet resource builder.</param>
    /// <param name="port">The destination port range (e.g., "443", "80-443"). Defaults to "*" (any).</param>
    /// <param name="from">The source address prefix (e.g., "AzureLoadBalancer", "Internet", "10.0.0.0/8"). Defaults to "*" (any).</param>
    /// <param name="to">The destination address prefix. Defaults to "*" (any).</param>
    /// <param name="protocol">The network protocol. Defaults to <see cref="SecurityRuleProtocol.Asterisk"/> (any).</param>
    /// <param name="priority">The rule priority (100-4096). If not specified, auto-increments from 100 by 100.</param>

View on GitHub (pinned to 25830f84bd)