'pulumi export statement in a loop works unexpectedly

I used pulumi python to create multiple azure VMs.

Basically, I just put the example code from azure-py-webserver into a loop, of course, each resource has its unique name with the index of the loop. I expected the export statement at the end of the loop would show me the public IPs of all VMs after they were created.

for i in range(2):
    vm_name = f"sol{i}"
    ...
    ...
    ...
    public_ip_addr = vm.id.apply(lambda _: network.get_public_ip_address_output(
        public_ip_address_name=public_ip.name,
        resource_group_name=resource_group.name))

    export(f"{vm_name} IP", public_ip_addr.ip_address)

All resources were created successfully, but the export output always showed a same IP for all VMs as below.

    Type                                      Name            Status
 +   pulumi:pulumi:Stack                       azure-vms-dev   created
 +   ├─ azure-native:resources:ResourceGroup   resource_group  created
 +   ├─ azure-native:network:VirtualNetwork    sol_VNET        created
 +   ├─ azure-native:network:PublicIPAddress   sol0_PublicIP   created
 +   ├─ azure-native:network:PublicIPAddress   sol1_PublicIP   created
 +   ├─ azure-native:network:NetworkInterface  sol0_Nic        created
 +   ├─ azure-native:network:NetworkInterface  sol1_Nic        created
 +   ├─ azure-native:compute:VirtualMachine    sol1            created
 +   └─ azure-native:compute:VirtualMachine    sol0            created

Outputs:
    sol0 IP:: "20.239.154.16"
    sol1 IP:: "20.239.154.16"

Resources:
    + 9 created

Duration: 1m55s

My question is, how could I export the public IPs of all VMs?



Solution 1:[1]

I just fixed this issue.

As @Frassle pointed out, the root cause of this issue is that I used the loop's local variable in the lambda expression directly.

After I studied How to Use Python Lambda Functions and the Inputs and Outputs of Pulumi, I think I should get the required variable from the argument of the apply method.

Here is the modified code:

for i in range(2):
    vm_name = f"sol{i}"
    ...
    ...
    ...
    # Get IP address as an output.
    combined_output = Output.all(
        vm_id=vm.id,
        public_ip_name=public_ip.name,
        resource_group_name=resource_group.name,
    )
    public_ip_addr = combined_output.apply(
        lambda args: network.get_public_ip_address_output(
            public_ip_address_name=args["public_ip_name"],
            resource_group_name=args["resource_group_name"],
        )
    )
    export(f"{vm_name} IP", public_ip_addr.ip_address)

Now I can get all the Public IPs I want:

Outputs:
  - sol0 IP: "20.239.193.78"
  - sol1 IP: "20.239.195.132"

Solution 2:[2]

Exports only happen once, at the end of a program. If you want to export more than one value, you can simply create an array or dict, append the values to that data structure and export that. So for example:

public_ip_address = [] # create an array to store the information
for i in range(2):
    vm_name = f"sol{i}"
    ...
    ...
    ...
    public_ip_addr = vm.id.apply(lambda _: network.get_public_ip_address_output(
        public_ip_address_name=public_ip.name,
        resource_group_name=resource_group.name))
    public_ip_addresses.append(public_ip_addr)


export(f"addresses", public_ip_addresses)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 flyisland
Solution 2 jaxxstorm