Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Circular dependency while creating an Application Gateway #3287

Open
carlospega opened this issue May 14, 2024 · 3 comments
Open

Circular dependency while creating an Application Gateway #3287

carlospega opened this issue May 14, 2024 · 3 comments
Labels
awaiting-feedback Blocked on input from the author kind/question Questions about existing features

Comments

@carlospega
Copy link

What happened?

I'm trying to create an ApplicationGateway and it seems like there are a couple of circular dependencies. The first problem i encounter is that for creating an application gateway i need to define an array of http_listeners. And because http_listener can't be created apart from the app gateway, and because it asks for frontend_ip_configuration: Input[SubResourceArgs] | None = None. I don't know how to procede.

class ApplicationGateway(
    ....
    frontend_ip_configurations: Input[Sequence[Input[InputType[ApplicationGatewayFrontendIPConfigurationArgs]]]] | None = None,
   ...
    http_listeners: Input[Sequence[Input[InputType[ApplicationGatewayHttpListenerArgs]]]] | None = None,
class ApplicationGatewayFrontendIPConfigurationArgs(
    *,
    id: Input[str] | None = None,
    name: Input[str] | None = None,
    private_ip_address: Input[str] | None = None,
    private_ip_allocation_method: Input[str | IPAllocationMethod] | None = None,
    private_link_configuration: Input[SubResourceArgs] | None = None,
    public_ip_address: Input[SubResourceArgs] | None = None,
    subnet: Input[SubResourceArgs] | None = None
)
class ApplicationGatewayHttpListenerArgs(
    ...
    frontend_ip_configuration: Input[SubResourceArgs] | None = None,
)

The same thing happens with the routing rule.
There's no way to create both object independently and then attach rules to the application gaateway.

Also, i tried constructing the id and reference it using network.SubResourceArgs(id="/subscriptions/<subid>/resourceGroups/<resourceGroupName>/providers/Microsoft.Network/applicationGateways/my-app-gateway/frontendIPConfigurations/my-frontend-ip-configurtion", but the problem is that names are appended a suffix at creation time, so i can't infer what the name will be. (Also i think the id is checked at creation time, so it wouldn't exist anyway).

Example

from pulumi import export
from pulumi_azure_native import resources, network

# Create a resource group
resource_group = resources.ResourceGroup("my-resource-group")

# Create a Virtual Network
vnet = network.VirtualNetwork(
    'my-vnet',
    resource_group_name=resource_group.name,
    address_space=network.AddressSpaceArgs(
        address_prefixes=['10.0.0.0/16']
    )
)

# Create a new subnet for the Application Gateway
subnet = network.Subnet(
    "my-subnet",
    resource_group_name=resource_group.name,
    virtual_network_name=vnet.name,
    address_prefix="10.0.2.0/24", # 256 ips
)

# Create a public IP address for the load balancer
public_ip = network.PublicIPAddress(
    "my-public-ip",
    resource_group_name=resource_group.name,
    public_ip_allocation_method=network.IPAllocationMethod.STATIC,
    sku=network.PublicIPAddressSkuArgs(
        name=network.PublicIPAddressSkuName.STANDARD,
        tier=network.PublicIPAddressSkuTier.REGIONAL
    )
)

# Define a gateway_ip_configuration for the Application Gateway
gateway_ip_configuration = network.ApplicationGatewayIPConfigurationArgs(
    name="my-gateway-ip-configuration",
    subnet=network.SubResourceArgs(id=subnet.id)
)

# Define a frontend_ip_configuration for the Application Gateway
frontend_ip_configuration = network.ApplicationGatewayFrontendIPConfigurationArgs(
    name="my-frontend-ip-configurtion",
    public_ip_address=network.SubResourceArgs(id=public_ip.id)
)

# Define backend address pool. Assume 3 containers already in place
backend_address_pool = network.ApplicationGatewayBackendAddressPoolArgs(
    name="my-backend-address-pool",
    backend_addresses=[
        network.ApplicationGatewayBackendAddressArgs(
            ip_address='10.0.1.4'
        ),
        network.ApplicationGatewayBackendAddressArgs(
            ip_address='10.0.1.5'
        ),
        network.ApplicationGatewayBackendAddressArgs(
            ip_address='10.0.1.6'
        ),
    ]
)

# Define backend HTTP settings
backend_http_settings = network.ApplicationGatewayBackendHttpSettingsArgs(
    name="my-backend-http-settings",
    port=80,
    protocol="Http",
    cookie_based_affinity="Disabled",
    request_timeout=20,
)

http_listener = network.ApplicationGatewayHttpListenerArgs(
    name="appGatewayHttpListener",
    frontend_ip_configuration=network.SubResourceArgs(id="/subscriptions/<subid>/resourceGroups/my-resource-groupc7f72802/providers/Microsoft.Network/applicationGateways/my-app-gateway/frontendIPConfigurations/my-frontend-ip-configurtion"),
    frontend_port=network.SubResourceArgs(id="/subscriptions/<subid>/resourceGroups/my-resource-groupc7f72802/providers/Microsoft.Network/applicationGateways/my-app-gateway/frontendPorts/my-frontend-port"),
    protocol="Http",
    ssl_certificate=None
)

# Define request routing rule
request_routing_rule = network.ApplicationGatewayRequestRoutingRuleArgs(
    name="appGatewayRule",
    rule_type=network.ApplicationGatewayRequestRoutingRuleType.BASIC,
    http_listener=network.SubResourceArgs(id="/subscriptions/<subid>/resourceGroups/my-resource-groupc7f72802/providers/Microsoft.Network/applicationGateways/my-app-gateway/httpListeners/appGatewayHttpListener"),
    backend_address_pool=network.SubResourceArgs(id="????????"),
    backend_http_settings=network.SubResourceArgs(id="????????"),
    url_path_map=None,
    priority=1
)

app_gateway = network.ApplicationGateway(
    "my-app-gateway",
    resource_group_name=resource_group.name,
    sku=network.ApplicationGatewaySkuArgs(
        name=network.ApplicationGatewaySkuName.STANDARD_V2,
        tier=network.ApplicationGatewayTier.STANDARD_V2,
        capacity=1
    ),
    gateway_ip_configurations=[gateway_ip_configuration],
    frontend_ip_configurations=[frontend_ip_configuration],
    frontend_ports=[network.ApplicationGatewayFrontendPortArgs(
        name="my-frontend-port",
        port=80
    )],
    backend_address_pools=[backend_address_pool],
    backend_http_settings_collection=[backend_http_settings],
    http_listeners=[http_listener],
    # request_routing_rules=[request_routing_rule],
)

# Export the DNS name of the application gateway
export("app_gateway_ip_address", public_ip.ip_address)

Output of pulumi about

CLI          
Version      3.115.2
Go Version   go1.22.2
Go Compiler  gc

Plugins
KIND      NAME          VERSION
resource  azure         5.75.1
resource  azure-native  2.40.0
resource  docker        4.5.3
language  python        unknown
resource  random        4.16.1

Host     
OS       darwin
Version  14.4.1
Arch     x86_64

This project is written in python: executable='/Users/carlospega/Documents/projects.nosync/interviews/mews/nginx/venv/bin/python3' version='3.12.3'

Current Stack: carlospega/app-gateway/dev

TYPE                                  URN
pulumi:pulumi:Stack                   urn:pulumi:dev::app-gateway::pulumi:pulumi:Stack::app-gateway-dev
pulumi:providers:azure-native         urn:pulumi:dev::app-gateway::pulumi:providers:azure-native::default_2_40_0
azure-native:resources:ResourceGroup  urn:pulumi:dev::app-gateway::azure-native:resources:ResourceGroup::my-resource-group
azure-native:network:VirtualNetwork   urn:pulumi:dev::app-gateway::azure-native:network:VirtualNetwork::my-vnet
azure-native:network:PublicIPAddress  urn:pulumi:dev::app-gateway::azure-native:network:PublicIPAddress::my-public-ip
azure-native:network:Subnet           urn:pulumi:dev::app-gateway::azure-native:network:Subnet::my-subnet


Found no pending operations associated with dev

Backend        
Name           pulumi.com
URL            https://app.pulumi.com/carlospega
User           carlospega
Organizations  carlospega
Token type     personal

Dependencies:
NAME                 VERSION
pip                  24.0
pulumi_azure         5.75.1
pulumi_azure_native  2.40.0
pulumi_docker        4.5.3
pulumi_random        4.16.1
setuptools           69.5.1
wheel                0.43.0

Additional context

No response

Contributing

Vote on this issue by adding a 👍 reaction.
To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

@carlospega carlospega added kind/bug Some behavior is incorrect or out of spec needs-triage Needs attention from the triage team labels May 14, 2024
@danielrbradley
Copy link
Member

Hi @carlospega thanks for reaching out. Yes, Azure have set up their APIs in a way that do cause various circular dependencies.

Here's a blog post from a while back which discusses these kinds of issues: https://www.pulumi.com/blog/exploring-circular-dependencies/

This is also descussed in the following issues:

Currently, the best approach is to do two deployments on the first run, one to create the resources without the cycle. Then a second deployment to update the second resource with a link back to the first as described in this comment: pulumi/pulumi#3021 (comment)

@danielrbradley danielrbradley added kind/question Questions about existing features and removed kind/bug Some behavior is incorrect or out of spec needs-triage Needs attention from the triage team labels May 15, 2024
@carlospega
Copy link
Author

hi @danielrbradley thanks for the answer. I'm not sure i understand correctly. The problem is that the API expects all the values at the first execution, so i'm not being able to create the application gateway on the first run because of errors like:

Code="ApplicationGatewayMustHaveAtleastOneResourceOfType" Message="At least one HttpListener resource must be specified for the Application Gateway /subscriptions/<subid>/resourceGroups/my-resource-group57bf397d/providers/Microsoft.Network/applicationGateways/my-app-gateway0876017f." Details=[]

But the application gateway my-app-gateway0876017f is not being created and in the second execution it changes the name.

The http_listener is required and it takes a frontend_ip_configuration reference which i can't create separately.

Any idea on how to workaround this?

Thanks!

@mikhailshilkov
Copy link
Member

Hi @carlospega

There is a way to solve this fairly simply by using relative IDs instead of full ones. You can use a special token $self to denote the parent resource name, so instead of

network.SubResourceArgs(id="/subscriptions/<subid>/resourceGroups/<resourceGroupName>/providers/Microsoft.Network/applicationGateways/my-app-gateway/frontendIPConfigurations/my-frontend-ip-configurtion")

you do

network.SubResourceArgs(id="$self/frontendIPConfigurations/my-frontend-ip-configurtion")

You can see a full example in https://github.com/pulumi/pulumi-azure-native/blob/master/examples/py-loadbalancer/__main__.py#L37-L92

@mikhailshilkov mikhailshilkov added the awaiting-feedback Blocked on input from the author label May 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting-feedback Blocked on input from the author kind/question Questions about existing features
Projects
None yet
Development

No branches or pull requests

3 participants