Hi there, wondering if anyone has experience with ...
# general
g
Hi there, wondering if anyone has experience with Azure ApplicationGateway using azure-native (https://www.pulumi.com/docs/reference/pkg/azure-native/network/applicationgateway/) and writing the templates in C#. I’m having a lot of trouble resolving the Ids for child components such as
HttpListeners.FrontendConfiguration
. I have separating
ApplicationGatewayFrontendIPConfigurationArgs
into its own variable, defining it ahead of the ApplicationGateway, and then attempting to use
new SubResourceArgs { Id: frontendConfig.Id }
to get the Id, but this doesn’t appear to resolve before it is needed (Which makes sense since it’s part of the ApplicationGateway) I have also tried predicting the Ids ahead of time, but I realised this won’t work since I don’t know what the Id of the ApplicationGateway is going to be yet. I’m sure I’m probably missing something silly and I will feel stupid when I work it out, but this has had me stumped…
Hopefully the lack of context is ok. I have an extract from my code to hopefully make it more clear what I’m trying to do
Copy code
var frontendIpConfiguration = new Network.Inputs.ApplicationGatewayFrontendIPConfigurationArgs
{
    Name = "public",
    PrivateIPAllocationMethod = "Dynamic",
    PublicIPAddress = new Network.Inputs.SubResourceArgs { Id = ipAddress.Id }
};
var frontendPort = new Network.Inputs.ApplicationGatewayFrontendPortArgs
{
    Name = "http",
    Port = 80
};
var httpListener = new Network.Inputs.ApplicationGatewayHttpListenerArgs
{
    Name = "static-web",
    FrontendIPConfiguration = new Network.Inputs.SubResourceArgs { Id = frontendIpConfiguration.Id.Apply(i => {
        Console.WriteLine($"ID Resolved as {i}");
        return i;
    }) },
    FrontendPort = new Network.Inputs.SubResourceArgs { Id = frontendPort.Id },
    Protocol = "http",
    HostName = record.Fqdn,
    RequireServerNameIndication = false
};
var backendAddressPool = new Network.Inputs.ApplicationGatewayBackendAddressPoolArgs
{
    Name = "static-web",
    BackendAddresses = {
        new Network.Inputs.ApplicationGatewayBackendAddressArgs
        {
            Fqdn = endpointOrigin
        }
    }
};
var backendHttpSettings = new Network.Inputs.ApplicationGatewayBackendHttpSettingsArgs
{
    Name = "static-web",
    Port = 443,
    Protocol = "Https",
    CookieBasedAffinity = Network.ApplicationGatewayCookieBasedAffinity.Disabled,
    PickHostNameFromBackendAddress = false,
    HostName = endpointOrigin,
    Path = "/"
    // RequestTimeout = 30,
    // Probe
};

var appGateway = new Network.ApplicationGateway(
    name: gatewayName,
    args: new Network.ApplicationGatewayArgs
    {
        ResourceGroupName = rg.Name,
        Sku = new Network.Inputs.ApplicationGatewaySkuArgs
        {
            Name = Network.ApplicationGatewaySkuName.Standard_Medium,
            Tier = Network.ApplicationGatewayTier.Standard,
            Capacity = 1
        },
        GatewayIPConfigurations = {
            new Network.Inputs.ApplicationGatewayIPConfigurationArgs
            {
                Name = "public",
                Subnet = new Network.Inputs.SubResourceArgs { Id = subnet.Id }
            }
        },
        FrontendIPConfigurations = { frontendIpConfiguration },
        FrontendPorts = { frontendPort },
        BackendAddressPools = { backendAddressPool },
        BackendHttpSettingsCollection = { backendHttpSettings },
        HttpListeners = { httpListener },
        RequestRoutingRules = {
            new Network.Inputs.ApplicationGatewayRequestRoutingRuleArgs
            {
                Name = "rr-static-web",
                RuleType = Network.ApplicationGatewayRequestRoutingRuleType.Basic,
                HttpListener = new Network.Inputs.SubResourceArgs { Id = httpListener.Id },
                BackendAddressPool = new Network.Inputs.SubResourceArgs { Id = backendAddressPool.Id },
                BackendHttpSettings = new Network.Inputs.SubResourceArgs { Id = backendHttpSettings.Id }
            }
        }
    },
    options: new CustomResourceOptions { Parent = this, DependsOn = { ipAddress, vnet, subnet } }
);
Which never logs
Console.WriteLine($"ID Resolved as {i}");
and gives the following errors…
Copy code
[
  {
    "code": "MissingJsonReferenceId",
    "message": "Value for reference id is missing. Path properties.httpListeners[0].properties.frontendIPConfiguration."
  },
  {
    "code": "MissingJsonReferenceId",
    "message": "Value for reference id is missing. Path properties.httpListeners[0].properties.frontendPort."
  },
  {
    "code": "MissingJsonReferenceId",
    "message": "Value for reference id is missing. Path properties.requestRoutingRules[0].properties.backendAddressPool."
  },
  {
    "code": "MissingJsonReferenceId",
    "message": "Value for reference id is missing. Path properties.requestRoutingRules[0].properties.backendHttpSettings."
  },
  {
    "code": "MissingJsonReferenceId",
    "message": "Value for reference id is missing. Path properties.requestRoutingRules[0].properties.httpListener."
  }
]
Which all checks out with what I would expect 🤷
c
Hi @gorgeous-ability-78921 I was playing with FrontDoor trying to make it work which appears to have the same "issue" as the App. GW with the sub-resource IDs. In my understanding, your approach to generate the ID is correct.
Copy code
//Create frontend IP and ports
var frontendPorts = new List<Pulumi.AzureNative.Network.Inputs.ApplicationGatewayFrontendPortArgs>
{
	new Pulumi.AzureNative.Network.Inputs.ApplicationGatewayFrontendPortArgs
	{
		Name = "appGwFpHttps", Port = 443,
	}
};

...

FrontendPort = new Pulumi.AzureNative.Network.Inputs.SubResourceArgs
{
	Id = Output.Format($"/subscriptions/{currentSubscriptionId}/resourceGroups/{args.ResourceGroupName}/providers/Microsoft.Network/applicationGateways/{args.Name}/frontendPorts/appGwFpHttps"),
},
The sub-resource IDs should be unique within the context of the resource in your case the App. GW. The uniqueness of each sub-resource is ensured by the last part in the example above the 'appGwFpHttps' which you need to control manually.
g
Yeah, that’s the path I ended up going with too