Answers: <p>The <code>cloudinit</code> provider only knows how to <em>encode</em> multipart MIME documents to pass to cloud init; there is no mechanism for <em>parsing</em> to modify and then re-encode.</p>
  <p>However, a different way to frame your problem is of constructing a sequence of part objects in a local value and then passing them all together to the <code>cloudinit_config</code> data source, like this:</p>
  <pre><code>locals {
    core_config = tolist([
      {
        content_type = &quot;text/x-shellscript&quot;
        content      = file(&quot;${path.module}/scripts/core_script1.sh&quot;)
      },
      {
        content_type = &quot;text/x-shellscript&quot;
        content      = file(&quot;${path.module}/scripts/core_script2.sh&quot;)
      },
    ])
    custom_config = tolist([
      {
        content_type = &quot;text/x-shellscript&quot;
        content      = file(&quot;${path.module}/scripts/custom_script.sh&quot;)
      },
    ])
  }
  
  data &quot;cloudinit_config&quot; &quot;core&quot; {
    dynamic &quot;part&quot; {
      for_each = local.core_config
      content {
        content_type = part.value.content_type
        content      = part.value.content
      }
    }
  }
  
  data &quot;cloudinit_config&quot; &quot;custom&quot; {
    dynamic &quot;part&quot; {
      for_each = concat(
        local.core_config,
        local.custom_config,
      )
      content {
        content_type = part.value.content_type
        content      = part.value.content
      }
    }
  }
  
  resource &quot;aws_instance&quot; &quot;vm1&quot; {
    instance_type = &quot;t2.small&quot;
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # ...
  
    user_data = data.cloudinit_config.core.rendered
  }
  
  resource &quot;aws_instance&quot; &quot;vm2&quot; {
    instance_type = &quot;t2.small&quot;
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # ...
  
    user_data = data.cloudinit_config.custom.rendered
  }
  </code></pre>
  <p>The above is using <a href="https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks" rel="nofollow noreferrer"><code>dynamic</code> blocks</a> to generate a dynamic number of <code>part</code> blocks based on the number of elements in a list, which means you can focus on making sure the lists have the necessary items, using whatever dynamic expressions you need to decide that, and have Terraform project that into zero or more <code>part</code> blocks automatically.</p>
  <p><strong>Note:</strong> the <code>hashicorp/template</code> provider has been deprecated and continues to be available only for backward-compatibility. For that reason, I wrote the above to use <a href="https://registry.terraform.io/providers/hashicorp/cloudinit/latest" rel="nofollow noreferrer">the <code>hashicorp/cloudinit</code> provider</a> instead. Its <a href="https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/cloudinit_config" rel="nofollow noreferrer"><code>cloudinit_config</code> data source</a> is based on what was formerly <code>template_cloudinit_config</code>.</p>
  
Answers: CC BY-SA 4.0
Answers: 2020-11-23T22:38:38.323Z
Answers: 2020-11-23T22:38:38.323Z
Answers: Martin Atkins
Body: <p>I recently discovered <code>template_cloudinit_config</code> within Terraform, and I'm hoping I can utilize it to specify something like the code below. Basically my goal is to declare a core <code>template_cloudinit_config</code> that contains a number of shell scripts that should get run on every machine.</p>
  <p>Then I'd like to declare a 2nd <code>template_cloudinit_config</code> that, without duplicating code, lets me essentially inherit the core set of scripts that should get applied to every instance AND define an additional script to run.</p>
  <p>That way from a compute perspective, I could easily create specialized instances that have custom scripts applied to them, but still inherit a number of basic configurations. I know I could make this work with remote-exec provisioners, but I am trying to avoid them since <a href="https://www.terraform.io/docs/provisioners/" rel="nofollow noreferrer">Terraform says they should be used as a last resort</a>.</p>
  <pre><code>data &quot;template_cloudinit_config&quot; &quot;core_config&quot; {
    part {
      content_type = &quot;text/x-shellscript&quot;
      content      = file(&quot;scripts/core_script1.sh&quot;)
    }
  
    part {
      content_type = &quot;text/x-shellscript&quot;
      content      = file(&quot;scripts/core_script2.sh&quot;)
    }
  }
  
  data &quot;template_cloudinit_config&quot; &quot;custom_config&quot; {
    part {
      content     = &quot;${data.template_cloudinit_config.core_config.rendered}&quot;
    }
  
    part {
      content_type = &quot;text/x-shellscript&quot;
      content      = file(&quot;scripts/custom_script.sh&quot;)
    }
  }
  
  resource &quot;aws_instance&quot; &quot;vm1&quot; {
    instance_type = &quot;t2.small&quot;
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # Other variables that declare subnet, key, etc
  
    user_data = &quot;${data.template_cloudinit_config.core_config.rendered}&quot;
  }
  
  resource &quot;aws_instance&quot; &quot;vm2&quot; {
    instance_type = &quot;t2.small&quot;
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # Other variables that declare subnet, key, etc
  
    user_data = &quot;${data.template_cloudinit_config.custom_config.rendered}&quot;
  }
  </code></pre>
  
Tags: <terraform><amazon-ec2>
Title: Can you inherit a template_cloudinit_config in another template_cloudinit_config object in Terraform?
UserDisplayName: ION28
ThreadText: # Can you inherit a template_cloudinit_config in another template_cloudinit_config object in Terraform?
  
  (Asked by: username_0 on 2020-11-22)
  
  I recently discovered `template_cloudinit_config` within Terraform, and I'm hoping I can utilize it to specify something like the code below. Basically my goal is to declare a core `template_cloudinit_config` that contains a number of shell scripts that should get run on every machine.
  
  Then I'd like to declare a 2nd `template_cloudinit_config` that, without duplicating code, lets me essentially inherit the core set of scripts that should get applied to every instance AND define an additional script to run.
  
  That way from a compute perspective, I could easily create specialized instances that have custom scripts applied to them, but still inherit a number of basic configurations. I know I could make this work with remote-exec provisioners, but I am trying to avoid them since [Terraform says they should be used as a last resort](https://www.terraform.io/docs/provisioners/).
  
  ```
  data "template_cloudinit_config" "core_config" {
    part {
      content_type = "text/x-shellscript"
      content      = file("scripts/core_script1.sh")
    }
  
    part {
      content_type = "text/x-shellscript"
      content      = file("scripts/core_script2.sh")
    }
  }
  
  data "template_cloudinit_config" "custom_config" {
    part {
      content     = "${data.template_cloudinit_config.core_config.rendered}"
    }
  
    part {
      content_type = "text/x-shellscript"
      content      = file("scripts/custom_script.sh")
    }
  }
  
  resource "aws_instance" "vm1" {
    instance_type = "t2.small"
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # Other variables that declare subnet, key, etc
  
    user_data = "${data.template_cloudinit_config.core_config.rendered}"
  }
  
  resource "aws_instance" "vm2" {
    instance_type = "t2.small"
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # Other variables that declare subnet, key, etc
  
    user_data = "${data.template_cloudinit_config.custom_config.rendered}"
  }
  
  ```
  
  **Tags:** terraform, amazon-ec2
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2020-11-23. Score: 2 | Accepted answer
  
  The `cloudinit` provider only knows how to *encode* multipart MIME documents to pass to cloud init; there is no mechanism for *parsing* to modify and then re-encode.
  
  However, a different way to frame your problem is of constructing a sequence of part objects in a local value and then passing them all together to the `cloudinit_config` data source, like this:
  
  ```
  locals {
    core_config = tolist([
      {
        content_type = "text/x-shellscript"
        content      = file("${path.module}/scripts/core_script1.sh")
      },
      {
        content_type = "text/x-shellscript"
        content      = file("${path.module}/scripts/core_script2.sh")
      },
    ])
    custom_config = tolist([
      {
        content_type = "text/x-shellscript"
        content      = file("${path.module}/scripts/custom_script.sh")
      },
    ])
  }
  
  data "cloudinit_config" "core" {
    dynamic "part" {
      for_each = local.core_config
      content {
        content_type = part.value.content_type
        content      = part.value.content
      }
    }
  }
  
  data "cloudinit_config" "custom" {
    dynamic "part" {
      for_each = concat(
        local.core_config,
        local.custom_config,
      )
      content {
        content_type = part.value.content_type
        content      = part.value.content
      }
    }
  }
  
  resource "aws_instance" "vm1" {
    instance_type = "t2.small"
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # ...
  
    user_data = data.cloudinit_config.core.rendered
  }
  
  resource "aws_instance" "vm2" {
    instance_type = "t2.small"
    ami           = data.aws_ami.ubuntu_ami.image_id
    
    # ...
  
    user_data = data.cloudinit_config.custom.rendered
  }
  
  ```
  
  The above is using [`dynamic` blocks](https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks) to generate a dynamic number of `part` blocks based on the number of elements in a list, which means you can focus on making sure the lists have the necessary items, using whatever dynamic expressions you need to decide that, and have Terraform project that into zero or more `part` blocks automatically.
  
  **Note:** the `hashicorp/template` provider has been deprecated and continues to be available only for backward-compatibility. For that reason, I wrote the above to use [the `hashicorp/cloudinit` provider](https://registry.terraform.io/providers/hashicorp/cloudinit/latest) instead. Its [`cloudinit_config` data source](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/cloudinit_config) is based on what was formerly `template_cloudinit_config`.
  
  
  ---
  
  