PSArm icon indicating copy to clipboard operation
PSArm copied to clipboard

Make resource referencing easier

Open rjmholt opened this issue 3 years ago • 4 comments

Today to reference one resource from another I have to embed the ARM the hard way:

Resource 'MyResource' -Provider Microsoft.Network -Type 'virtualNetworks/subnets' -ApiVersion 2019-11-01 {
    properties {
        addressPrefix 10.0.0.0/24
    }
}

Resource (concat $namePrefix '-nic') -ApiVersion 2019-11-01 -Provider Microsoft.Network -Type networkInterfaces {
    properties {
        ipConfigurations {
            name 'myConfig'
            properties {
                privateIPAllocationMethod -Value Dynamic
                subnet {
                    # HERE: This is cumbersome
                    id (resourceId 'Microsoft.Network/networkInterfaces' (concat $vnetNamespace $namePrefix '-subnet'))
                }
            }
        }
    }
}

Instead we need a really simple way to use the referential object concept to embed a reference from one resource to another.

We have the IArmReferenceable interface and concept to power this, the question is simply how to make it.

Forcing users to assign a prior resource to a variable is a bad experience, but is the most straightforward way. Otherwise resources could automagically define variables based in some way on their name, but their name might not be known at publication time, it might clobber another variable and it's not discoverable in any case.

rjmholt avatar Mar 26 '21 19:03 rjmholt

Thinking of the editing experience is there not a way we can as the template is being written (in vscode I am assuming here) that we could have a dynamically expanding variable that you can tabcomplete all these types of painful things from something like this

$ARM.Resource.MyResource.ResourceID

Or is that going to be unworkable?

kilasuit avatar Mar 27 '21 01:03 kilasuit

$ARM.Resource.MyResource.ResourceID

That's an interesting idea.

There are two challenges:

  • The actual template won't exist yet, so we'd need to create a variable that can dynamically accommodate these assignments, enter them as sort of slots in the template and then after the template is evaluated do a pass over it and fill them in. That's possible but requires a combination of the magic of the way we do ARM parameters and the way we do ARM functions
  • It's allowed to name resources with an ARM expression that isn't statically known (like [concat(variables('x'), '-subnet')]), which makes the MyResource part tricky. Whereas if the resource is defined with a variable in some way, it becomes easy to pass around by reference.

One way I could imagine is like this:

Resource ... -ResourceVariable myResource {
}

...

subnet {
    id $myResource
}

rjmholt avatar Mar 27 '21 04:03 rjmholt

It's allowed to name resources with an ARM expression that isn't statically known (like [concat(variables('x'), '-subnet')]), which makes the MyResource part tricky. Whereas if the resource is defined with a variable in some way, it becomes easy to pass around by reference.

This is one reason why when I've been writing ARM templates lately I try and write them like this upfront (simplified example)

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "myparam": {
            "type": "string",
            "metadata": {
                "description": "description"
            }
        },
        "VMConfig": {
            "type": "object",
            "metadata": {
                "description": "description"
            }
        }
    },
    "functions": [],
    "variables": {
        "Var1": "[concat(parameters('myparam'), '-',deployment().location)]",
        "Deployments": {
            "Names": {
                "VM": "[concat('VM-',variables('Var1'))]"
            },
            "APIVersions": {
                "Compute": "2019-07-01"
            }
        }
    },
    "resources": [
        {
            "name": "[variables('Deployments').Names.VM]",
            "type": "Microsoft.Compute/virtualMachines",
            "apiVersion": "[variables('Deployments').APIVersions.Compute]",
            "location": "[resourceGroup().location]",
            "dependsOn": [
            ],
            "tags": {
            },
            "properties": {
                "hardwareProfile": {
                    "vmSize": "[parameters('VMConfig').hardwareProfile.vmSize]"
            },
                "osProfile": "[parameters('VMConfig').osProfile]",
                "storageProfile": "[parameters('VMConfig').storageProfile]",
                "networkProfile": "[parameters('VMConfig').networkProfile]",
                "diagnosticsProfile": "[parameters('VMConfig').diagnosticsProfile]"
            }
        }
    ],
    "outputs": {}
}

This allows for the object being passed through to optionally be tested outside to this that said, I make heavy use of Linked Templates for code reusability whereas others may use other methods

kilasuit avatar Mar 28 '21 14:03 kilasuit

This is potentially a separate feature but once this exists I think it’s not a crazy jump to support automatic dependencies. Coming from terraform, this makes the config a lot more streamlined and for a human is obvious. If I create a vnet and a subnet and the subnet is in that vnet it’s obvious that a dependency exists there

rdbartram avatar Apr 01 '21 22:04 rdbartram