hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.6k stars 4.64k forks source link

azurerm resourceid interpolation function #1466

Open richeney opened 6 years ago

richeney commented 6 years ago

Community Note

Description

The Azure resource IDs are a fixed format, and it would be useful to have an function to convert from the full string to a map of the constituent parts.

An Azure specific interpolation function (or perhaps data source), which takes an argument of either:

  1. a string for an Azure "resourceId"
  2. a list for [ "subscriptionId", "resourceGroupName", "Microsoft.Provider/type", "resourceName" ]

(If possible, make the first two in the list optional. If resource group is not specified then default to the resource group for the current resource stanza (if that context is held). If subscription is not specified then default to the current subscription.)

The function is pure string manipulation (unless subscription / resource group defaults are possible), but it would help massively.

Example resourceids

For a resource group, resource and sub-resource

/subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/resourceGroups/rgname
/subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/resourceGroups/rgname/providers/Microsoft.Network/virtualNetworks/resname
/subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/resourceGroups/rgname/providers/Microsoft.Network/virtualNetworks/resname/subnets/subresname

Output map

Using /subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/resourceGroups/rgname/providers/Microsoft.Network/virtualNetworks/resname as the input example, then the output map would look something like:

  "id"                   = "/subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/resourceGroups/rgname/providers/Microsoft.Network/virtualNetworks/resname"
  "subscription"         = "2d31be49-d999-4415-bb65-8aec2c90ba62"
  "resource_group"       = "rgname"
  "namespace"            = "Microsoft.Network/virtualNetworks"
  "resource"             = "resname"
  "subscription_scope"   = "/subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62"
  "resource_group_scope" = "/subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/resourceGroups/rgname"
}

If there is a subresource (e.g. subnets) then the resource value should be everything after the resource provider namespace, e.g. "resname/subnets/subresname"

New or Affected Resource(s)

Potential Terraform Configuration

# Copy-paste your Terraform configurations here - for large Terraform configs,
# please use a service like Dropbox and share a link to the ZIP file. For
# security, you can also encrypt the files using our GPG public key.

References

AdamCoulterOz commented 3 years ago

@tombuildsstuff thanks for pointing this out to me ... I might try creating a PR for this on the weekend... am I correct in assuming it should be implemented as a data resource?

avishnyakov commented 3 years ago

Late to the party, but there might be a quick workaround with azurerm_client_config. Should also help with https://github.com/hashicorp/terraform-provider-azurerm/issues/5691 and https://github.com/hashicorp/terraform-provider-azurerm/pull/12323

Might need tweaks for multi-provider setup, but still not too bad. Feel free to improve this one.

# assuming there is a list of all_resources

locals {
  all_resources = [
    {
      name    = "resource-1"
      rg_name = "rg-name-1"
    },
    {
      name    = "resource-2"
      rg_name = "rg-name-2"
    }
  ]

  all_resource_ids = merge(
    # forming resource id string by hand, since this is not supported in azurerm_resources data source yet
    # "id" = "/subscriptions/xyz/resourceGroups/rgname/providers/Microsoft.Network/virtualNetworks/abc"

    {
      for k, v in data.azurerm_resources.target_resources : v.name => {
        name = v.name,
        id = join("/", [
          # subscription
          "/subscriptions",
          data.azurerm_client_config.current.subscription_id,

          # resource groups
          "resourceGroups",
          v.resource_group_name,

          # providers (resource type)
          "providers",
          v.resources[0].type,

          // name
          v.resources[0].name
        ]),
      }
    },
  )
}

# get all resources
data "azurerm_resources" "target_resources" {
  for_each = { for value in local.all_resources :
    value.name => value
  }

  resource_group_name = each.value.rg_name
  name                = each.value.name
}

# get current azure client config
data "azurerm_client_config" "current" {
}
AdamCoulterOz commented 3 years ago

I created a temporary provider while waiting for support from azurerm

https://registry.terraform.io/providers/AdamCoulterOz/azurehelpers/latest

data "azurehelpers_resource_id" "example" {
  resource_id = "resourceId"
}

it lets you access the following:

data.azurehelpers_resource_id.example.subscription_id
data.azurehelpers_resource_id.example.resource_group_name
data.azurehelpers_resource_id.example.provider_namespace
data.azurehelpers_resource_id.example.resource_type
data.azurehelpers_resource_id.example.name
data.azurehelpers_resource_id.example.parent_resources
data.azurehelpers_resource_id.example.full_resource_type
AdamCoulterOz commented 2 years ago

@tombuildsstuff, how are those new parsers going?

harshavmb commented 1 year ago

Thanks @AdamCoulterOz ,

After disappointing azurerm_resources, I must admit your provider helped me.

azurerm_resources is very inconsistent & unreliable during deployments. I don't find resources even minutes after their creation. sleep & other depends_on hacks didn't improve as well.

Looks like Azure APIs behave differently for read & create/update functions depending on the node they end up. Atleast in North Europe I see this randomness.

If you could tweak your provider a bit further to fetch version from APIs & append to full_resource_type, azapi_resource data provider could be used which I find very reliable.

AdamCoulterOz commented 1 year ago

My stop-gap data resource explicitly doesn't try to get the resource, it just parses the ID, because a user won't always have read permission at every scope. Maybe only at the child scope. I can have access to a resource, but not it's resource group for example.

I was thinking of cleaning it up a little and submitting it as a PR to the new azapi provider. They have a nice way of passing in the resource type so it could return a cleaner map of fields.

harshavmb commented 1 year ago

Exactly! Parsing is all I need in my child module. The prime reason is create/update functions use parent resource ID & always worked.

Before I used the data provider to parse RG, type in the child module which failed randomly due to eventual consistency problems.

In my case, child modules refer to private endpoint resource & calling resources are managed resources like storage account, key vault etc.,

Please don't do the cleanup :)

richeney commented 1 year ago

Yep, there is no need to read the resourceIds from Azure. This is purely string manipulation which is why I originally suggested a string function as a possibility.

Ideally using the azurerm provider model would extend the set of functions with Azure specific functions - e.g. resourceid() - as well as the azurerm data sources and resource types, but I don't think the provider framework allows that.

tombuildsstuff commented 1 year ago

@richeney

Ideally using the azurerm provider model would extend the set of functions with Azure specific functions - e.g. resourceid() - as well as the azurerm data sources and resource types, but I don't think the provider framework allows that.

yeah, having dug into this we likely won't be adding a resourceid function for this - instead I think we'd likely look towards something like generating a Data Source for each Resource ID (hypothetically, azurerm_resource_group_id, or similar)

BHoggs commented 6 months ago

So, now that Terraform core supports exposing provider functions (https://github.com/hashicorp/terraform/pull/34394) - perhaps time to revisit this one?

richeney commented 6 months ago

100% agree. Parsing the Azure resource IDs is a perfect use case for a provider function.

tombuildsstuff commented 6 months ago

@BHoggs @richeney

FWIW we've spent some time thinking about this recently, but unfortunately a generic Resource ID parsing function would quickly become problematic/a source of user configuration issues due the random recasings that happen in the Azure API, and whilst we're working to get a handle on those, for that reason we'd be unlikely to ship a generic function. It's worth noting that there's also some of the more problematic scenarios, such as Resource IDs containing multiple segments of the same name (e.g. two providers segments) or scoped Resource IDs which make things interesting - and as such you ultimately need context of what you're parsing when you're parsing it, and is why we're moving away from the generic Resource ID functions internally in favour of a function with context of what it's parsing.

That said, I suspect having functions to generate a Resource ID would be useful (e.g. template_resource_group_id("subscriptionid", "resourcegroup") and an associated parse function (e.g. parse_resource_group_id("")) could be useful - as such whilst we won't likely add a generic Resource ID interpolation/parsing function - this route would likely make more sense.

All that to say, we should look into this, but a generic Resource ID interpolation/parsing function is unfortunately more problematic than it first appears, even if it's be useful in the happy path - and so it's more likely this'd ship in the form of more specific Resource ID interpolation/parsing functions, rather than as a generic Resource ID function.

Thanks!

richeney commented 6 months ago

For reference, ARM / Bicep have the following functions that generate resourceIds in this space:

These all generate the different scope level resourceIDs. Some use defaults from context for parameters such as subscription when unspecified.

AdamCoulterOz commented 6 months ago

... meanwhile:

https://registry.terraform.io/providers/AdamCoulterOz/azurehelpers/latest

Screenshot 2024-05-07 at 11 39 24 PM