Retrieve an item from an object, then manipulate it

Hi all,
Firstly, excuse me if the question seems silly. My excuse, I have come from an Ops background :worried:

I am working with the “azurerm_log_analytics_solution” resource and it seems that the solution_name must match the product name.
For example the product would be OMSGallery/Updates and the solution_name must be Updates.

My challenge is to extract “Updates” from product.

I have declared this variable to be used;

variable “plan” {
type = list(object(
{
publisher = string
product = string
}
))

default = [
    {
        publisher = "Microsoft"
        product   = "OMSGallery/Updates"
    }
]

}

I am struggling to extract “Updates” from this variable.

I have tried various functions and using the Terraform console this is easily done when not tackling an object;element(split("/","Product/Updates"),1)

Using Terraform v0.12.26

Any help would be appreciated.
Thanks.

Hi @CrowersEDF,

It seems like you are on the right track here: splitting on / and taking the second element (index one) is indeed a reasonable way to extract the second part of those slash-separated strings.

The remaining challenge is how to do that systematically for all of your elements of var.plan. Unfortunately there are numerous ways to do that and which one to choose depends a lot on what you ultimately intend to do with this value, and because I’m not familiar with azurerm_log_analytics_solution I don’t feel qualified to guess what that would look like.

Could you share an example of roughly what you’re expecting the azurerm_log_analytics_solution resource configuration to look like? Don’t worry about getting the syntax exactly right… I’m just hoping to see where the different parts of this var.plan fit in.

Thanks for the reply ApparentlyMart.
here is the configuration;

resource “azurerm_log_analytics_solution” “law_solution_updates” {
solution_name = element(split("/", var.plan.value.product), 1) # hardcoding “Updates” works, whereas any other name hardcoded string does not.
location = var.location
resource_group_name = var.resource_group_name
workspace_resource_id = data.azurerm_log_analytics_workspace.law.id
workspace_name = data.azurerm_log_analytics_workspace.law.name

dynamic “plan” {
for_each = var.plan
content {
publisher = plan.value.publisher
product = plan.value.product
}
}
}

the issue with this resource is that the solution_name must be “Updates”, in this working example.
Running terraform plan doesnt throw up an error, the following error only occurs after confirming the apply

pollingTrackerBase#updateRawBody: failed to unmarshal response body: StatusCode=0 – Original Error: invalid character ‘F’ looking for beginning of value on …\main.tf

Therefore, I need to extract “Updates” from from the product argument. I dont really want to create a separate variable if I can help it.

The syntax error I get with the current config is;

Error: Unsupported attribute
*** on main.tf line 13, in resource “azurerm_log_analytics_solution” “law_solution_updates”:***
*** 13: solution_name = element(split("/", var.plan.value.product), 1)***
This value does not have any attributes.

Thanks for your support.

Hi @CrowersEDF. Thanks for that additional context!

I was pretty baffled by the design of this resource, because in your example it looks like there would be one plan block per entry in var.plan, but the solution_name isn’t in the plan block, so was unclear to me what value it ought to take if there were more than one element of var.plan with different suffixes.

In the end I had to go have a look at the provider source code to understand how this resource type is supposed to work. It looks like the provider only expects a single plan block, so I think the repetition must be of the resource as a whole rather than the plan block in particular.

That suggests something like this:

resource “azurerm_log_analytics_solution” “law_solution_updates” {
  for_each = {
    for p in var.plan : "${p.publisher} ${p.product}" => p
  }

  solution_name         = split("/", each.value.product)[1]
  location              = var.location
  resource_group_name   = var.resource_group_name
  workspace_resource_id = data.azurerm_log_analytics_workspace.law.id
  workspace_name        = data.azurerm_log_analytics_workspace.law.name

  plan {
    publisher = each.value.publisher
    product   = each.value.product
  }
}

Resource for_each requires each instance to have a unique identifier string associated with it so Terraform can track the instances, so for the sake of this example I just put the publisher and product together into a single string, which will produce addresses like this:

  • azurerm_log_analytics_solution.law_solution_updates["Microsoft OMSGallery/Updates"]

This identifier format means that if you edit the publisher or product of an existing object then Terraform will understand it as the intent to destroy an existing log analytics solution and create a new one, rather than to edit in-place. If you want to be able to edit in-place then you’d need to give each element an explicit unique identifier inside your configuration, which you could do by changing variable "plan" to be a map instead of a list and then assign that map directly to the for_each argument.

Wow, thanks for taking the time to research this for me.
I did notice that after adding a new item in to the list and it bombed out for the reason you mention.

I haven’t had chance to test your code block yet, I will update you to let you know how it goes.

I would never have thought of doing this though;

azurerm_log_analytics_solution.law_solution_updates["Microsoft OMSGallery/Updates"]

I have a lot to learn :+1:t3:

Hey @apparentlymart, just following up.
After some testing (and playing so I understand what is going on), I am happy with this approach and thought it would be courteous so say.
Thanks for taking the time out to reply and I’m grateful for the little education along the way…