• HashiCorp Developer

  • HashiCorp Cloud Platform
  • Terraform
  • Packer
  • Consul
  • Vault
  • Boundary
  • Nomad
  • Waypoint
  • Vagrant
Terraform
  • Install
  • Tutorials
    • About the Docs
    • Configuration Language
    • Terraform CLI
    • Terraform Cloud
    • Terraform Enterprise
    • CDK for Terraform
    • Provider Use
    • Plugin Development
    • Registry Publishing
    • Integration Program
  • Registry(opens in new tab)
  • Try Cloud(opens in new tab)
  • Sign up
Plugin Development

Framework

Skip to main content
  • Framework
  • Provider Servers
    • Terraform Concepts
    • Schemas
    • Attributes
    • Blocks
    • Paths
    • Path Expressions
    • Accessing Terraform Data
    • Writing Data
    • Conversion Rules
    • Custom Types
  • Returning Errors and Warnings
  • Validation
  • Acceptance Tests
  • Debugging

  • Resources

  • Tutorial Library
  • Certifications
  • Community Forum
    (opens in new tab)
  • Support
    (opens in new tab)
  • GitHub
    (opens in new tab)
  • Terraform Registry
    (opens in new tab)
  1. Developer
  2. Terraform
  3. Plugin Development
  4. Framework
  5. Handling Data
  6. Paths
  • Plugin Framework
  • v1.0.x
  • v0.17.x
  • v0.16.x
  • v0.15.x
  • v0.14.x
  • v0.13.x
  • v0.12.x
  • v0.11.x
  • v0.10.x
  • v0.9.x
  • v0.8.x
  • v0.7.x

»Paths

An exact location within a schema or schema-based data such as tfsdk.Config, tfsdk.Plan, or tfsdk.State, is referred to as a path.

Usage

Example uses in the framework include:

  • Diagnostics intended for a specific attribute, which allows Terraform to show the configuration file and line information.
  • Accessing configuration, plan, or state data for a specific attribute.
  • Writing plan or state data for a specific attribute.

More advanced use cases, such as path based attribute validators, typically implement path expressions which enables additional logic beyond exact paths.

Concepts

Paths are designed around the underlying Terraform implementation of a schema and schema-based data. Terraform schemas are a tree structure based on value storing attributes, which may have their own structural component, and structural blocks. Both attributes and blocks are associated with specific data types, some of which represent structural or collection types which hold further information which can be traversed. Each traversal into that further information is referred to as a path step. Paths are always absolute and start from the root, or top level, of a schema.

Given the tree structure of Terraform schemas, descriptions of paths and their steps borrow certain hierarchy terminology such as parent and child. A parent path describes a path without one or more of the final steps of a given path, or put differently, a partial path closer to the root of the schema. A child path describes a path with one or more additional steps beyond a given path, or put differently, a path containing the given path but further from the root of the schema.

Every path must align with the schema definition or an error diagnostic will be raised when working with paths within the framework. Provider-defined functionality that is schema-based, such as attribute validation and attribute plan modification, are provided an accurate current path since that functionality would not be able to determine its own path.

Path expressions are an abstraction on top of paths, which enable additional use cases with provider-defined functionality, such as relative path, parent step, and wildcard step support.

Building Paths

The framework implementation for paths is in the path package, with the path.Path type being the main provider developer interaction point. Call the path.Root() function with an attribute name or block name at the root of the schema to begin a path.

Given this example schema with a root attribute named example_root_attribute:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "example_root_attribute": schema.StringAttribute{
            Required: true,
        },
    },
}

The call to path.Root() which matches the location of example_root_attribute string value is:

path.Root("example_root_attribute")

For blocks, the beginning of a path is similarly defined. Attribute and block names cannot overlap, so the framework automatically handles whether a path is referring to an attribute or block to start.

Given this example schema with a root block named example_root_block:

schema.Schema{
    Blocks: map[string]schema.Block{
        "example_root_block": schema.ListNestedBlock{
            NestedObject: schema.NestedBlockObject{/* ... */},
        },
    },
}

The call to path.Root() which matches the location of example_root_block list value is:

path.Root("example_root_block")

Once a path.Path is started, it supports a builder pattern, which allows for chaining method calls to construct a full path.

This example shows a hypothetical path that points to the first element of a list attribute to highlight the builder pattern:

path.Root("example_list_attribute").AtListIndex(0)

This pattern can be extended to as many calls as necessary. The different framework schema types and their associated path step methods are shown in the following sections.

Building Attribute Paths

The following table shows the different path.Path type methods associated with building paths for attribute implementations. Attribute types that cannot be traversed further are shown with N/A (not applicable).

Attribute TypeChild Path Method
schema.BoolAttributeN/A
schema.Float64AttributeN/A
schema.Int64AttributeN/A
schema.ListAttributeAtListIndex()
schema.MapAttributeAtMapKey()
schema.NumberAttributeN/A
schema.ObjectAttributeAtName()
schema.SetAttributeAtSetValue()
schema.StringAttributeN/A

Given following schema example:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "root_map_attribute": schema.MapAttribute{
            ElementType: types.StringType,
            Required: true,
        },
    },
}

The path which matches the string value associated with a hypothetical map key of example-key of the root_map_attribute attribute is:

path.Root("root_map_attribute").AtMapKey("example-key")

Any type that supports a child path may define an element type that also supports a child path. Paths can continue to be built using the associated method with each level of the attribute type.

Given following schema example:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "root_map_attribute": schema.MapAttribute{
            ElementType: types.ListType{
                ElemType: types.StringType,
            },
            Required: true,
        },
    },
}

The path which matches the third string value associated with the list value of a hypothetical map key of example-key of the root_map_attribute attribute is:

path.Root("root_map_attribute").AtMapKey("example-key").AtListIndex(2)

Unless there is a very well defined data structure involved, the level of path specificity in the example above is fairly uncommon to manually build in provider logic though. Most provider logic will typically build a path to the value of an attribute (e.g. its first type) and work with that data, or potentially one level deeper in well known cases, since each level introduces additional complexity associated with potentially null or unknown values. Provider logic can instead use an iterative path building approach when dealing with attributes that have multiple levels.

This example shows an iterative path building approach to handle any map keys and list indices in the above schema:

attributePath := path.Root("root_map_attribute")

// attributeValue is an example types.Map value which was previously fetched,
// potentially using the path above.
for mapKey, mapValue := range attributeValue.Elements() {
    mapKeyPath := attributePath.AtMapKey(mapKey)

    // ...

    for listIndex, listValue := range mapValue.Elements() {
        listIndexPath := mapKeyPath.AtListIndex(listIndex)

        // ...
    }
}

Building Nested Attribute Paths

The following table shows the different path.Path type methods associated with building paths for nested attributes.

Nested Attribute TypeChild Path Method
schema.ListNestedAttributeAtListIndex()
schema.MapNestedAttributeAtMapKey()
schema.SetNestedAttributeAtSetValue()
schema.SingleNestedAttributeAtName()

Nested attributes eventually implement attributes at child paths, which follow the methods shown in the Building Attribute Paths section.

Building List Nested Attributes Paths

An attribute that implements schema.ListNestedAttribute conceptually is a list containing objects with attribute names.

Given the following schema example:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "root_list_attribute": schema.ListNestedAttribute{
            NestedObject: schema.NestedAttributeObject{
                Attributes: map[string]schema.Attribute{
                    "nested_string_attribute": schema.StringAttribute{
                        Required: true,
                    },
                },
            },
            Required: true,
        },
    },
}

The path which matches the list associated with the root_list_attribute attribute is:

path.Root("root_list_attribute")

The path which matches the first object in the list associated with the root_list_attribute attribute is:

path.Root("root_list_attribute").AtListIndex(0)

The path which matches the nested_string_attribute string value in the first object in the list associated with root_list_attribute attribute is:

path.Root("root_list_attribute").AtListIndex(0).AtName("nested_string_attribute")

Building Map Nested Attributes Paths

An attribute that implements schema.MapNestedAttribute conceptually is a map containing values of objects with attribute names.

Given the following schema example:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "root_map_attribute": schema.MapNestedAttribute{
            NestedObject: schema.NestedAttributeObject{
                Attributes: map[string]schema.Attribute{
                    "nested_string_attribute": schema.StringAttribute{
                        Required: true,
                    },
                },
            },
            Required: true,
        },
    },
}

The path which matches the map associated with the root_map_attribute attribute is:

path.Root("root_map_attribute")

The path which matches the a hypothetical "example-key" object in the map associated with the root_map_attribute attribute is:

path.Root("root_map_attribute").AtMapKey("example-key")

The path which matches the nested_string_attribute string value in a hypothetical "example-key" object in the map associated with root_map_attribute attribute is:

path.Root("root_map_attribute").AtMapKey("example-key").AtName("nested_string_attribute")

Building Set Nested Attributes Paths

An attribute that implements schema.SetNestedAttribute conceptually is a set containing objects with attribute names. Attempting to build set nested attribute paths is extremely tedius as set element selection is based on the entire value of the element, including any null or unknown values. Avoid manual set-based path building. Instead, use functionality which supports path expressions as that supports wildcard path matching (path.Expression type AtAnySetValue() method) or relative paths.

Given the following schema example:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "root_set_attribute": schema.SetNestedAttribute{
            NestedObject: schema.NestedAttributeObject{
                Attributes: map[string]schema.Attribute{
                    "nested_string_attribute": schema.StringAttribute{
                        Required: true,
                    },
                },
            },
            Required: true,
        },
    },
}

The path which matches the set associated with the root_set_attribute attribute is:

path.Root("root_set_attribute")

Examples below will presume a nested_string_attribute string value of types.StringValue("example") for brevity. In real world usage, the string value may be types.StringNull(), types.StringUnknown() or types.StringValue("something-else"), which are all considered different set paths from each other. Each additional attribute or block introduces exponentially more possible paths given each attribute or block value may be null, unknown, or a unique known value.

The path which matches the object associated with the root_set_attribute block is:

path.Root("root_set_attribute").AtSetValue(types.ObjectValueMust(
    map[string]attr.Type{
        "nested_string_attribute": types.StringType,
    },
    map[string]attr.Value{
        "nested_string_attribute": types.StringValue("example"),
    }
))

The path which matches the nested_string_attribute string value associated with root_set_attribute block is:

path.Root("root_set_attribute").AtSetValue(types.ObjectValueMust(
    map[string]attr.Type{
        "nested_string_attribute": types.StringType,
    },
    map[string]attr.Value{
        "nested_string_attribute": types.StringValue("example"),
    }
)).AtName("nested_string_attribute")

Building Single Nested Attributes Paths

An attribute that implements schema.SingleNestedAttribute conceptually is an object with attribute names.

Given the following schema example:

schema.Schema{
    Attributes: map[string]schema.Attribute{
        "root_grouped_attributes": schema.SingleNestedAttribute{
            Attributes: map[string]schema.Attribute{
                "nested_string_attribute": schema.StringAttribute{
                    Required: true,
                },
            },
            Required: true,
        },
    },
}

The path which matches the object associated with the root_grouped_attributes attribute is:

path.Root("root_grouped_attributes")

The path which matches the nested_string_attribute string value in the object associated with the root_grouped_attributes attribute is:

path.Root("root_grouped_attributes").AtName("nested_string_attribute")

Building Block Paths

The following table shows the different path.Path type methods associated with building paths for blocks.

Block TypeChild Path Method
schema.ListNestedBlockAtListIndex()
schema.SetNestedBlockAtSetValue()
schema.SingleNestedBlockAtName()

Blocks can implement nested blocks. Paths can continue to be built using the associated method with each level of the block type.

Blocks eventually implement attributes at child paths, which follow the methods shown in the Building Attribute Paths section. Blocks cannot contain nested attributes.

Building List Block Paths

A block defined with schema.ListNestedBlock conceptually is a list containing objects with attribute or block names.

Given following schema example:

schema.Schema{
    Blocks: map[string]schema.Block{
        "root_list_block": schema.ListNestedBlock{
            NestedObject: schema.NestedBlockObject{
                Attributes: map[string]schema.Attribute{
                    "block_string_attribute": schema.StringAttribute{
                        Required: true,
                    },
                },
                Blocks: map[string]schema.Block{
                    "nested_list_block": schema.ListNestedBlock{
                        Attributes: map[string]schema.Attribute{
                            "nested_block_string_attribute": schema.StringAttribute{
                                Required: true,
                            },
                        },
                    },
                },
            },
        },
    },
}

The path which matches the list associated with the root_list_block block is:

path.Root("root_list_block")

The path which matches the first object in the list associated with the root_list_block block is:

path.Root("root_list_block").AtListIndex(0)

The path which matches the block_string_attribute string value in the first object in the list associated with root_list_block block is:

path.Root("root_list_block").AtListIndex(0).AtName("block_string_attribute")

The path which matches the nested_list_block list in the first object in the list associated with root_list_block block is:

path.Root("root_list_block").AtListIndex(0).AtName("nested_list_block")

The path which matches the nested_block_string_attribute string value in the first object in the list associated with the nested_list_block list in the first object in the list associated with root_list_block block is:

path.Root("root_list_block").AtListIndex(0).AtName("nested_list_block").AtListIndex(0).AtName("nested_block_string_attribute")

Building Set Block Paths

A block defined with schema.SetNestedBlock conceptually is a set containing objects with attribute or block names. Attempting to build set block paths is extremely tedius as set element selection is based on the entire value of the element, including any null or unknown values. Avoid manual set-based path building. Instead, use functionality which supports path expressions as that supports wildcard path matching (path.Expression type AtAnySetValue() method) or relative paths.

Given following schema example:

schema.Schema{
    Blocks: map[string]schema.Block{
        "root_set_block": schema.SetNestedBlock{
            NestedObject: schema.NestedBlockObject{
                Attributes: map[string]schema.Attribute{
                    "block_string_attribute": schema.StringAttribute{
                        Optional: true,
                    },
                },
            },
        },
    },
}

The path which matches the set associated with the root_set_block block is:

path.Root("root_set_block")

Examples below will presume a block_string_attribute string value of types.StringValue("example") for brevity. In real world usage, the string value may be types.StringNull(), types.StringUnknown() or types.StringValue("something-else"), which are all considered different set paths from each other. Each additional attribute or block introduces exponentially more possible paths given each attribute or block value may be null, unknown, or a unique known value.

The path which matches the object associated with the root_set_block block is:

path.Root("root_set_block").AtSetValue(types.ObjectValueMust(
    map[string]attr.Type{
        "block_string_attribute": types.StringType,
    },
    map[string]attr.Value{
        "block_string_attribute": types.StringValue("example"),
    }
))

The path which matches the block_string_attribute string value associated with root_set_block block is:

path.Root("root_set_block").AtSetValue(types.ObjectValueMust(
    map[string]attr.Type{
        "block_string_attribute": types.StringType,
    },
    map[string]attr.Value{
        "block_string_attribute": types.StringValue("example"),
    }
)).AtName("block_string_attribute")

Building Single Block Paths

A block defined with schema.SingleNestedBlock conceptually is an object with attribute or block names.

Given following schema example:

schema.Schema{
    Blocks: map[string]schema.Block{
        "root_single_block": schema.SingleNestedBlock{
            Attributes: map[string]schema.Attribute{
                "block_string_attribute": schema.StringAttribute{
                    Required: true,
                },
            },
            Blocks: map[string]schema.Block{
                "nested_single_block": schema.SingleNestedBlock{
                    Attributes: map[string]schema.Attribute{
                        "nested_block_string_attribute": schema.StringAttribute{
                            Required: true,
                        },
                    },
                },
            },
        },
    },
}

The path which matches the object associated with the root_single_block block is:

path.Root("root_single_block")

The path which matches the block_string_attribute string value in the object associated with root_single_block block is:

path.Root("root_single_block").AtName("block_string_attribute")

The path which matches the nested_single_block object in the object associated with root_single_block block is:

path.Root("root_single_block").AtName("nested_single_block")

The path which matches the nested_block_string_attribute string value in the object associated with the nested_single_block in the object associated with root_single_block block is:

path.Root("root_single_block").AtName("nested_single_block").AtName("nested_block_string_attribute")
Edit this page on GitHub

On this page

  1. Paths
  2. Usage
  3. Concepts
  4. Building Paths
Give Feedback(opens in new tab)
  • Certifications
  • System Status
  • Terms of Use
  • Security
  • Privacy
  • Trademark Policy
  • Trade Controls
  • Give Feedback(opens in new tab)