A layer definition is an entry in a JSON object. This entry includes a name, a list of dependencies, and the contents of the Terraform files Layerform will use to spawn infrastructure.

Creating layer definitions

To create a layer definition, you need to start by creating a layerform.json file in the root folder of your project. It’s in that folder that you’ll run layerform configure to provision layers to the back-end.

In that file, each object within the layers array contains a layer definition with a name, list of files, and, optionally, a list of dependencies.

{
    "layers": [
        {
            "name": "base_example",
            "files": ["layers/base.tf", "layers/base/**"]
        },
        {
            "name": "your_top_layer",
            "files": ["layers/example.tf", "layers/example/**"],
            "dependencies": ["base_example"]
        }
    ]
}

Here’s what each key is for:

  1. name: an unique name for this layer definition. You’ll use this name in the CLI to spawn instances of this layer definition.
  2. files: a list of Terraform files included in this layer. Layerform will run these files to provision infrastructure for an instance of this layer definition.
  3. dependencies: a list layer definition names upon which instances of the current layer will be placed.

Once you have a layerform.json file, you should run layerform configure to upload the layer definitions to your Layerform back-end.

Creating Terraform files for layer definitions

By convention, you should encapsulate your layer definitions’ Terraform files into Terraform modules.

Encapsulating layer definitions into modules makes it easier to understand which files belong to each layer.

For example, if you have an elastic_stack layer, you’d have kibana.tf and elasticsearch.tf files within a folder called elastic_stack. That folder would also have any necessary inputs and outputs in inputs.tf and outputs.tf files.

layerform/
└─ elastic_stack/
    ├─ elasticsearch.tf
    ├─ kibana.tf
    ├─ inputs.tf
    └─ outputs.tf

Don’t forget to also create an entry-point instantiating your module.

layerform/
├─ elastic_stack/
│   ├─ elasticsearch.tf
│   ├─ kibana.tf
│   ├─ inputs.tf
│   └─ outputs.tf
└─ entrypoint.tf <--- uses the module elastic_stack

This entrypoint should instantiate the module and pass the necessary inputs to it.

module "elastic_stack" {
    source = "./modules/elastic_stack"
    cluster_id = modules.eks.cluster_id
}

Then, when creating the entry in layerform.json, make sure to include both the entrypoint and all the files in the module’s folder.

{
    "layers": [
        // Don't forget to also add the base layer here
        {
            "name": "elastic_stack",
            "files": ["layers/example.tf", "layers/example/**"],
            "dependencies": ["base"]
        }
    ]
}

Using variables across layer definitions

When using layer definitions, keep in mind that you can reference variables from another definition if, and only if, the reference variable belongs to the layer below.

For example, it’s fine if your elastic_stack layer must take an input which comes from the eks module which is part of the underlying eks layer.

module "elastic_stack" {
    source = "./modules/elastic_stack"

    # This cluster_id reference comes from a file which
    # belongs to the underlying `eks` layer. That's fine.
    cluster_id = modules.eks.cluster_id
}

On the other hand, it’s not fine if your eks layer references values from the elastic_stack layer on top of it.

# This code won't work
module "eks" {
    source = "./modules/eks"

    # This is NOT fine! Here, the reference comes from the
    # elastic_stack layer on *top*. Which won't be available.
    cluster_id = modules.elastic_stack.elasticsearch_url
}

Encapsulating layers into modules makes it easier to understand whether you’re referencing values in the layers below, above, or next to the target layer.

Using layer definitions

Layer definitions are used by the Layerform CLI so it knows which Terraform files to run to create a layer instance. Layerform also looks at the dependencies list to fetch state for any underlying layers.

To use a layer definition, you must configure your CLI with the back-end containing the desired definition. For that, you must edit the config.yaml file within ~/.layerform.

In that file, you should configure a context with the desired type and its settings. Then, you should set `currentContext to the desired context’s name.

currentContext: tutorial-context
contexts:
    tutorial-context:
        type: s3
        bucket: layerform-bucket-example

After that, you can spawn layers using layerform spawn <layer_definition_name> <desired_id>. The layerform spawn command will pull the matching definition from the specified context and run its Terraform files.

Updating or deleting layer definitions

To update a layer definition, you must update the Terraform files associated with it and run layerform configure again. That will cause the Layerform CLI to update the back-end with your updated files.

To delete a particular layer definition, you can simply remove the corresponding entry from your layerform.json file and run layerform configure again.