Read blocks in defined order

Hi,

I was wondering if there is a way to read blocks in the order they have been defined in tf file?
An example

resource "my_deployment_process" "deploymentproc" {
   script_action {
              name = "one"
   }
   container_action {
               name = "two"
   }
   script_action {
               name = "three"
   }
}

So in the above example, I want to read the script_action/one, container_action/two, script_action/three, in this order.
Currently, we read the blocks using something like:
d.GetOk("script_action") or d.GetOk("container_action")
but this does not give us the correct order as a whole.
Above, d is a d *schema.ResourceData, yes I am still using the older framework.

Any help would be appreciated.

Thank you
John

Although this ordering exists in the HCL input file, the relative ordering of different attributes/blocks has been discarded well before a provider can get to it.

I would actually go so far as to suggest that it might be better to represent a complex deployment pipeline using some other language in a separate file - perhaps using whatever native representation the deployment tool uses, rather than translating it into HCL so that it can be written in-line in a Terraform resource.

Terraform supports here-doc syntax for multi-line string literals, and the file function for loading text from accompanying files, so the user then has options as to where exactly they write the definition - and also the advantage of not needing to learn a different flavour of syntax to interact with this through Terraform.

Hi @johnsimons :wave:

As with any configuration language, there are certain design choices that the Terraform configuration language has taken. In general, the Terraform configuration language is mostly statically typed where the types determine the specific behaviors of a value. It sounds like you may be looking for an ordered collection of elements, which in Terraform is represented by a list. Since you mention things like *schema.ResourceData, I’m going to presume you developing the provider with terraform-plugin-sdk, which is totally okay, but does limit the available functionality as compared to developing on top of terraform-plugin-framework.

A potential solution using terraform-plugin-sdk in this case would be to wrap your steps in a “step” block. Since you mention they should be ordered, a list block seems appropriate. In this case, your schema may look like:

Schema: map[string]*schema.Schema{
  "step": {
    Type: schema.TypeList,
    Elem: &schema.Resource{
      Schema: map[string]*schema.Schema{
        "container_action": {/*...*/},
        "script_action": {/*...*/},
      },
    },
    // ...
  }
  // ...
}

To do the data handling, the d.Get("step") call will return []any results, which should be ordered map[string]any of objects. Practitioners can configure it via the below syntax or via dynamic block expressions:

resource "my_deployment_process" "deploymentproc" {
  step {
    script_action {
      name = "one"
    }
  }

  step {
    container_action {
      name = "two"
    }
  }

  step {
    script_action {
      name = "three"
    }
  }
}

I mention terraform-plugin-framework earlier because it enables the usage of nested attributes, which may feel more natural for the configuration and enables practitioners to use any expressions, such as for, etc.

resource "my_deployment_process" "deploymentproc" {
  steps = [
    {
      script_action = {
        name = "one"
      }
    },
    {
      container_action = {
        name = "two"
      }
    },
    {
      script_action = {
        name = "three"
      }
    },
  ]
}

# or configurations like the below which are much more awkward with dynamic blocks
variable "steps" {
  # ...
}

resource "my_deployment_process" "deploymentproc" {
  steps = var.steps
}

Hope this helps. :+1:

Hi @bflad,

This definitely helps, and thank you for all the examples and the new way to make this nicer using the new framework :smile:

thank you