Feature request: support provider functions
To support functions like
- parse azure resource id
- build azure resource id
refs: https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts
Other considerations here @ms-henglu can we do them? 3. location listing 4. flatten map function to simplify for each expressions General guidance by Terraform is to avoid anything that requires calls to the provider itself, which we should keep in mind. @matt-FFFFFF for FYI
Locations could work like this:
https://registry.terraform.io/modules/Azure/regions/azurerm/latest
Merging date from locations API and compute provider to determine if a location has zones support.
We should cache the API response in the provider to adhere to HashiCorp's recommendation. This means we get consistent results. Plus the data won't change that frequently.
@stemaMSFT @grayzu
Hello! Looking for any suggestions on the function to build Azure resource IDs. I think there are two options:
-
Allow users to provide
parentID,resource type, andnameto construct the resource ID, as described in this guide. Pros: quite straightforward & less parameters needed Cons: users may not know what the parent id is? -
Allow users to provide
resource type,name, and other specifics likesubscription IDandresource group nameetc..., and use those to construct theparentIDinternally, which is then used to build the full resource ID. Pros: referring to more specific names that user knows about Cons: too many parameters & a lot of cases to think about in building the parent id internally
@hqhqhqhqhqhqhqhqhqhqhq I think it would make sense to do option 1, since that would be consistent with the behavior of AzAPI (as parent_id is already a parameter in resource definitions). It keeps consistent with Azure as well, which is what we're aiming for philosophically with AzAPI.
I think we could have a few functions.
The option 1 as mentioned above plus dedicated functions for the four major scopes in azure:
Tenant - just need resource type and name
Management group - need MG id plus resource type and name
Subscription - need sub id, resource type and name
Resource group - sub id, RG name, resource type and name
For more complex scenarios we can use the parent id option and maybe nest the functions?
@matt-FFFFFF would you mind elaborating on why you prefer four separate functions compared to one function to construct resource IDs generically? Maybe with some scenarios. Would help us understand which direction to take.
@matt-FFFFFF would you mind elaborating on why you prefer four separate functions compared to one function to construct resource IDs generically? Maybe with some scenarios. Would help us understand which direction to take.
@stemaMSFT
Thinking more I think this could be one function, with a variadic input.
E.g.
provider::azapi::resource_id("resource_group", "<subscription id>", "<resource group name>", "<resource type>", "<resource name>")
provider::azapi::resource_id("management_group", "<management group id>", "<resource type>", "<resource name>")
provider::azapi::resource_id("tenant", "<resource type>", "<resource name>")
provider::azapi::resource_id("parent", "<parent resource id>" , "<resource type>", "<resource name>")
And so on. As long as each mode was identified by the first input I think that would work.
Makes sense to me. @ms-henglu if you don't have any other concerns, I think we can move forward with this approach.
Hi @matt-FFFFFF ,
Thanks for the suggestions! But unfortunately variadic function is supported by the Terraform provider function.
How about the below design proposed by @hqhqhqhqhqhqhqhqhqhqhq https://github.com/Azure/terraform-provider-azapi/pull/553#issue-2408065215
@matt-FFFFFF @stemaMSFT Adding to above, I think the difference lies where the scope is defined. We can:
- Put it a part of the function e.g. build_management_group_scope_resource_id
pros:
- no need for users to manually input "management_group" as a param
- easier to implement such function as the number of parameters are fixed
cons:
- more functions to maintain
- may need more functions if more scope types were introduced later
- Use a generic function, and let user give the scope type, e.g. provider::azapi::resource_id("management_group", "
", " ", " ")
pros:
- one single goto function for users
cons:
- user have explicity input the scope type, e,.g. "management_group"
- may become more complex given that different scopes needs different number of parameters
personally think having multiple functions can be more suitable, especially that it's not likely for more scope types to be introduced anytime soon, so it really just need 4-5 more functions
I just noticed that the bicep/ARM templates have similar functions: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-resource#subscriptionresourceid
So I'm proposing the following functions which are similar with the bicep's.
The # 1 function is just the provider function implementation of the azapi_resource_id data source. And the rest of them allow user to build the resource ID with only the "name" part, for example, the management group name, resource group name and each resource name. Users don't need to worry about the ID format.
And to support nested resource under different deployed scope, the last argument of these functions is a list of string. More details please see the below examples:
-
resourceId(resourceType, parentId, name)
Example:
resourceId("Microsoft.Network/virtualNetworks", "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1", "vnet1")
= "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1"
-
tenantResourceId(resourceType, [resourceName1, resourceName2, ...])
Example:
tenantResourceId("Microsoft.Billing/billingAccounts/billingProfiles", ["ba1", "bp1"])
= "/providers/Microsoft.Billing/billingAccounts/ba1/billingProfiles/bp1"
-
subscriptionResourceId(subscriptionId, resourceType, [resourceName1, resourceName2, ...])
Example:
subscriptionResourceId("00000000-0000-0000-0000-000000000000", "Microsoft.Resources/resourceGroups", ["rg1"])
= "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1"
-
managementGroupResourceId(managementGroupName, resourceType, [resourceName1, resourceName2, ...])
Example:
managementGroupResourceId("mg1", "Microsoft.Billing/billingAccounts/billingProfiles", ["ba1", "bp1"])
= "/providers/Microsoft.Management/managementGroups/mg1/providers/Microsoft.Billing/billingAccounts/ba1/billingProfiles/bp1"
-
extensionResourceId(resourceId, extensionName, [resourceName1, resourceName2, ...])
Example:
extensionResourceId("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1", "Microsoft.Authorization/locks", ["mylock"])
= "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/providers/Microsoft.Authorization/locks/mylock"
-
resourceGroupResourceId(subscriptionId, resourceGroupName, resourceType, [resourceName1, resourceName2, ...])
Example:
resourceGroupResourceId("00000000-0000-0000-0000-000000000000", "rg1", "Microsoft.Network/virtualNetworks/subnets", ["vnet1", "subnet1"])
= "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/subnet1"
Completed via https://github.com/Azure/terraform-provider-azapi/pull/553