---
page_title: 'Plugin Development - Framework: Paths'
description: >-
  How to implement paths in the provider development framework.
  Paths represent a location within a schema or schema-based data.
---

# Paths

An exact location within a [schema](/terraform/plugin/framework/handling-data/schemas) or schema-based data such as [`tfsdk.Config`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Config), [`tfsdk.Plan`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Plan), or [`tfsdk.State`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#State), is referred to as a path.

## Usage

Example uses in the framework include:

- [Diagnostics](/terraform/plugin/framework/diagnostics) intended for a specific attribute, which allows Terraform to show the configuration file and line information.
- [Accessing configuration, plan, or state data](/terraform/plugin/framework/accessing-values) for a specific attribute.
- [Writing plan or state data](/terraform/plugin/framework/writing-state) for a specific attribute.

More advanced use cases, such as [path based attribute validators](/terraform/plugin/framework/validation#path-based-attribute-validators), typically implement [path expressions](/terraform/plugin/framework/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](/terraform/plugin/framework/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](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path), with the [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) being the main provider developer interaction point. Call the [`path.Root()` function](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Root) 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`:

```go
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:

```go
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`:

```go
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:

```go
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:

```go
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](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for attribute implementations. Attribute types that cannot be traversed further are shown with N/A (not applicable).

| Attribute Type            | Child Path Method                                                                                      |
|---------------------------|--------------------------------------------------------------------------------------------------------|
| `schema.BoolAttribute`    | N/A                                                                                                    |
| `schema.DynamicAttribute` | N/A                                                                                                    |
| `schema.Float32Attribute` | N/A                                                                                                    |
| `schema.Float64Attribute` | N/A                                                                                                    |
| `schema.Int32Attribute`   | N/A                                                                                                    |
| `schema.Int64Attribute`   | N/A                                                                                                    |
| `schema.ListAttribute`    | `AtListIndex()`                                                                                        |
| `schema.MapAttribute`     | `AtMapKey()`                                                                                           |
| `schema.NumberAttribute`  | N/A                                                                                                    |
| `schema.ObjectAttribute`  | `AtName()`                                                                                             |
| `schema.SetAttribute`     | `AtSetValue()`                                                                                         |
| `schema.StringAttribute`  | N/A                                                                                                    |

Given following schema example:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for nested attributes.

| Nested Attribute Type          | Child Path Method |
| ------------------------------ | ----------------- |
| `schema.ListNestedAttribute`   | `AtListIndex()`   |
| `schema.MapNestedAttribute`    | `AtMapKey()`      |
| `schema.SetNestedAttribute`    | `AtSetValue()`    |
| `schema.SingleNestedAttribute` | `AtName()`        |

Nested attributes eventually implement attributes at child paths, which follow the methods shown in the [Building Attribute Paths section](#building-attribute-paths).

#### 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:

```go
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:

```go
path.Root("root_list_attribute")
```

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

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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](/terraform/plugin/framework/path-expressions) as that supports wildcard path matching ([`path.Expression` type `AtAnySetValue()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.AtAnySetValue)) or relative paths.

Given the following schema example:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

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

### Building Block Paths

The following table shows the different [`path.Path` type](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Path) methods associated with building paths for blocks.

| Block Type                 | Child Path Method |
| -------------------------- | ----------------- |
| `schema.ListNestedBlock`   | `AtListIndex()`   |
| `schema.SetNestedBlock`    | `AtSetValue()`    |
| `schema.SingleNestedBlock` | `AtName()`        |

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](#building-attribute-paths). 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:

```go
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:

```go
path.Root("root_list_block")
```

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

```go
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:

```go
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:

```go
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:

```go
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](/terraform/plugin/framework/path-expressions) as that supports wildcard path matching ([`path.Expression` type `AtAnySetValue()` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/path#Expression.AtAnySetValue)) or relative paths.

Given following schema example:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

```go
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:

```go
path.Root("root_single_block").AtName("nested_single_block").AtName("nested_block_string_attribute")
```

### Building Dynamic Attribute Paths

An attribute that implements `schema.DynamicAttribute` does not have a statically defined type, as the underlying value type is determined at runtime. When building paths for dynamic values, always target the root dynamic attribute.

Given the following schema example:

```go
schema.Schema{
	Attributes: map[string]schema.Attribute{
		"root_dynamic_attribute": schema.DynamicAttribute{
			Required: true,
		},
	},
}
```

The path which matches the dynamic value associated with the `root_dynamic_attribute` attribute is:

```go
path.Root("root_dynamic_attribute")
```
