Complex Attribute Types
Complex Attributes Overview
Complex attribute types enable sophisticated resource configurations that mirror complex API structures while maintaining user-friendly Terraform syntax.
Attribute Type Hierarchy
Primitive Types:
├── String
├── Number (Int64, Float64)
└── Bool
Collection Types:
├── List
├── Set
└── Map
Complex Types:
├── Single Nested Attribute
├── List Nested Attribute
├── Set Nested Attribute
├── Map Nested Attribute
└── Dynamic (runtime type)
Nested Attributes
Single Nested Attribute
For singular complex objects.
schema.SingleNestedAttribute{
Description: "Network configuration",
Required: true,
Attributes: map[string]schema.Attribute{
"vpc_id": schema.StringAttribute{
Description: "VPC identifier",
Required: true,
},
"subnet_ids": schema.ListAttribute{
Description: "Subnet identifiers",
Required: true,
ElementType: types.StringType,
},
"security_group_ids": schema.SetAttribute{
Description: "Security group identifiers",
Optional: true,
ElementType: types.StringType,
},
},
}
Terraform Usage
resource "cachecloud_cache" "example" {
name = "my-cache"
network_config = {
vpc_id = "vpc-12345"
subnet_ids = [
"subnet-abc",
"subnet-def"
]
security_group_ids = [
"sg-123",
"sg-456"
]
}
}
Model Struct
type CacheResourceModel struct {
Name types.String `tfsdk:"name"`
NetworkConfig *NetworkConfigModel `tfsdk:"network_config"`
}
type NetworkConfigModel struct {
VPCID types.String `tfsdk:"vpc_id"`
SubnetIDs types.List `tfsdk:"subnet_ids"`
SecurityGroupIDs types.Set `tfsdk:"security_group_ids"`
}
List Nested Attributes
For collections of complex objects.
Schema Definition
schema.ListNestedAttribute{
Description: "Node configurations",
Required: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"availability_zone": schema.StringAttribute{
Description: "Availability zone",
Required: true,
},
"instance_type": schema.StringAttribute{
Description: "Instance type",
Required: true,
},
"engine_version": schema.StringAttribute{
Description: "Engine version",
Optional: true,
Computed: true,
},
},
},
}
Usage
resource "cachecloud_cluster" "example" {
name = "my-cluster"
nodes = [
{
availability_zone = "us-west-2a"
instance_type = "cache.t3.micro"
engine_version = "6.2"
},
{
availability_zone = "us-west-2b"
instance_type = "cache.t3.micro"
engine_version = "6.2"
},
{
availability_zone = "us-west-2c"
instance_type = "cache.t3.small"
}
]
}
Model
type ClusterResourceModel struct {
Name types.String `tfsdk:"name"`
Nodes types.List `tfsdk:"nodes"` // List of NodeModel
}
type NodeModel struct {
AvailabilityZone types.String `tfsdk:"availability_zone"`
InstanceType types.String `tfsdk:"instance_type"`
EngineVersion types.String `tfsdk:"engine_version"`
}
Reading Values
func (r *ClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan ClusterResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
// Extract nodes from list
var nodes []NodeModel
resp.Diagnostics.Append(plan.Nodes.ElementsAs(ctx, &nodes, false)...)
if resp.Diagnostics.HasError() {
return
}
// Process each node
for i, node := range nodes {
tflog.Debug(ctx, "Processing node", map[string]interface{}{
"index": i,
"az": node.AvailabilityZone.ValueString(),
"type": node.InstanceType.ValueString(),
})
// Create node via API...
}
}
Set Nested Attributes
For unordered collections of unique objects.
Schema
schema.SetNestedAttribute{
Description: "Backup configurations",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"schedule": schema.StringAttribute{
Description: "Cron schedule",
Required: true,
},
"retention_days": schema.Int64Attribute{
Description: "Retention in days",
Required: true,
},
"enabled": schema.BoolAttribute{
Description: "Whether backup is enabled",
Optional: true,
Computed: true,
Default: booldefault.StaticBool(true),
},
},
},
}
Usage
resource "cachecloud_cache" "example" {
name = "my-cache"
backups = [
{
schedule = "0 2 * * *" # Daily at 2 AM
retention_days = 7
enabled = true
},
{
schedule = "0 0 * * 0" # Weekly on Sunday
retention_days = 30
}
]
}
Working with Sets
var backups []BackupModel
resp.Diagnostics.Append(plan.Backups.ElementsAs(ctx, &backups, false)...)
// Note: Order not guaranteed with sets
for _, backup := range backups {
tflog.Info(ctx, "Backup configuration", map[string]interface{}{
"schedule": backup.Schedule.ValueString(),
"retention": backup.RetentionDays.ValueInt64(),
})
}
Map Nested Attributes
For key-value pairs where values are complex objects.
Schema
schema.MapNestedAttribute{
Description: "Environment-specific configurations",
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"instance_count": schema.Int64Attribute{
Description: "Number of instances",
Required: true,
},
"instance_type": schema.StringAttribute{
Description: "Instance type",
Required: true,
},
"auto_scaling": schema.BoolAttribute{
Description: "Enable auto-scaling",
Optional: true,
},
},
},
}
Usage
resource "cachecloud_deployment" "example" {
name = "my-deployment"
environments = {
dev = {
instance_count = 1
instance_type = "cache.t3.micro"
auto_scaling = false
}
staging = {
instance_count = 2
instance_type = "cache.t3.small"
auto_scaling = true
}
production = {
instance_count = 5
instance_type = "cache.r6g.large"
auto_scaling = true
}
}
}
Model
type DeploymentResourceModel struct {
Name types.String `tfsdk:"name"`
Environments types.Map `tfsdk:"environments"` // Map of EnvironmentModel
}
type EnvironmentModel struct {
InstanceCount types.Int64 `tfsdk:"instance_count"`
InstanceType types.String `tfsdk:"instance_type"`
AutoScaling types.Bool `tfsdk:"auto_scaling"`
}
Processing Map
var environments map[string]EnvironmentModel
resp.Diagnostics.Append(plan.Environments.ElementsAs(ctx, &environments, false)...)
// Iterate over environments
for envName, envConfig := range environments {
tflog.Info(ctx, "Environment configuration", map[string]interface{}{
"environment": envName,
"count": envConfig.InstanceCount.ValueInt64(),
"type": envConfig.InstanceType.ValueString(),
})
// Create environment via API...
}
Deep Nesting
Multi-Level Nested Structures
schema.SingleNestedAttribute{
Description: "Advanced configuration",
Optional: true,
Attributes: map[string]schema.Attribute{
"networking": schema.SingleNestedAttribute{
Required: true,
Attributes: map[string]schema.Attribute{
"vpc": schema.SingleNestedAttribute{
Required: true,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Required: true,
},
"cidr_block": schema.StringAttribute{
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(
regexp.MustCompile(`^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$`),
"must be valid CIDR notation",
),
},
},
},
},
"subnets": schema.ListNestedAttribute{
Required: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Required: true,
},
"availability_zone": schema.StringAttribute{
Required: true,
},
"route_table": schema.SingleNestedAttribute{
Optional: true,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Required: true,
},
"routes": schema.ListNestedAttribute{
Optional: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"destination": schema.StringAttribute{
Required: true,
},
"target": schema.StringAttribute{
Required: true,
},
},
},
},
},
},
},
},
},
},
},
},
}
Usage
resource "cachecloud_infrastructure" "example" {
name = "my-infra"
advanced_config = {
networking = {
vpc = {
id = "vpc-12345"
cidr_block = "10.0.0.0/16"
}
subnets = [
{
id = "subnet-abc"
availability_zone = "us-west-2a"
route_table = {
id = "rtb-123"
routes = [
{
destination = "0.0.0.0/0"
target = "igw-456"
},
{
destination = "10.1.0.0/16"
target = "pcx-789"
}
]
}
}
]
}
}
}
Dynamic Types
For runtime-determined types.
Schema
schema.DynamicAttribute{
Description: "Flexible configuration",
Optional: true,
}
Usage
resource "cachecloud_config" "string_example" {
name = "string-config"
config = "simple-string"
}
resource "cachecloud_config" "map_example" {
name = "map-config"
config = {
key1 = "value1"
key2 = "value2"
}
}
resource "cachecloud_config" "list_example" {
name = "list-config"
config = ["item1", "item2", "item3"]
}
Handling Dynamic Types
func (r *ConfigResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan ConfigResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
// Get dynamic value
configValue := plan.Config
switch {
case configValue.IsNull():
// Handle null
case configValue.IsUnderlyingValueString():
var str string
resp.Diagnostics.Append(configValue.Get(ctx, &str)...)
// Handle string
case configValue.IsUnderlyingValueMap():
var m map[string]attr.Value
resp.Diagnostics.Append(configValue.Get(ctx, &m)...)
// Handle map
case configValue.IsUnderlyingValueList():
var list []attr.Value
resp.Diagnostics.Append(configValue.Get(ctx, &list)...)
// Handle list
}
}
Computed Nested Attributes
Schema with Computed Nested
schema.SingleNestedAttribute{
Description: "Cache statistics",
Computed: true,
Attributes: map[string]schema.Attribute{
"hit_rate": schema.Float64Attribute{
Description: "Cache hit rate percentage",
Computed: true,
},
"connections": schema.Int64Attribute{
Description: "Current connections",
Computed: true,
},
"memory_usage": schema.SingleNestedAttribute{
Computed: true,
Attributes: map[string]schema.Attribute{
"used_bytes": schema.Int64Attribute{
Computed: true,
},
"max_bytes": schema.Int64Attribute{
Computed: true,
},
"percentage": schema.Float64Attribute{
Computed: true,
},
},
},
},
}
Populating Computed Values
// After API call
stats := apiResponse.Statistics
statsModel := &StatisticsModel{
HitRate: types.Float64Value(stats.HitRate),
Connections: types.Int64Value(stats.Connections),
MemoryUsage: &MemoryUsageModel{
UsedBytes: types.Int64Value(stats.Memory.Used),
MaxBytes: types.Int64Value(stats.Memory.Max),
Percentage: types.Float64Value(float64(stats.Memory.Used) / float64(stats.Memory.Max) * 100),
},
}
state.Statistics = statsModel
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
Attribute Validators
Cross-Attribute Validation
schema.SingleNestedAttribute{
Description: "Backup configuration",
Optional: true,
Validators: []validator.Object{
objectvalidator.All(
// Custom validator
backupConfigValidator{},
),
},
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
Required: true,
},
"schedule": schema.StringAttribute{
Optional: true,
},
"retention_days": schema.Int64Attribute{
Optional: true,
},
},
}
type backupConfigValidator struct{}
func (v backupConfigValidator) Description(ctx context.Context) string {
return "Validates backup configuration consistency"
}
func (v backupConfigValidator) MarkdownDescription(ctx context.Context) string {
return "Validates backup configuration consistency"
}
func (v backupConfigValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) {
var config struct {
Enabled types.Bool `tfsdk:"enabled"`
Schedule types.String `tfsdk:"schedule"`
RetentionDays types.Int64 `tfsdk:"retention_days"`
}
resp.Diagnostics.Append(req.ConfigValue.As(ctx, &config, basetypes.ObjectAsOptions{})...)
if resp.Diagnostics.HasError() {
return
}
// If enabled, schedule and retention must be set
if config.Enabled.ValueBool() {
if config.Schedule.IsNull() {
resp.Diagnostics.AddAttributeError(
req.Path.AtName("schedule"),
"Missing Required Attribute",
"Schedule must be specified when backup is enabled",
)
}
if config.RetentionDays.IsNull() {
resp.Diagnostics.AddAttributeError(
req.Path.AtName("retention_days"),
"Missing Required Attribute",
"Retention days must be specified when backup is enabled",
)
}
}
}
Building Complex Values
Creating Nested Objects
// Create nested object from API response
networkConfig := &NetworkConfigModel{
VPCID: types.StringValue(apiResp.VPC.ID),
}
// Build subnet list
subnetValues := make([]attr.Value, 0, len(apiResp.Subnets))
for _, subnet := range apiResp.Subnets {
subnetValues = append(subnetValues, types.StringValue(subnet.ID))
}
var diags diag.Diagnostics
networkConfig.SubnetIDs, diags = types.ListValue(types.StringType, subnetValues)
resp.Diagnostics.Append(diags...)
// Build security group set
sgValues := make([]attr.Value, 0, len(apiResp.SecurityGroups))
for _, sg := range apiResp.SecurityGroups {
sgValues = append(sgValues, types.StringValue(sg.ID))
}
networkConfig.SecurityGroupIDs, diags = types.SetValue(types.StringType, sgValues)
resp.Diagnostics.Append(diags...)
state.NetworkConfig = networkConfig
Creating List of Objects
// Create list of node objects
nodeObjects := make([]NodeModel, 0, len(apiResp.Nodes))
for _, node := range apiResp.Nodes {
nodeObjects = append(nodeObjects, NodeModel{
AvailabilityZone: types.StringValue(node.AZ),
InstanceType: types.StringValue(node.Type),
EngineVersion: types.StringValue(node.Version),
})
}
// Convert to types.List
nodeType := types.ObjectType{
AttrTypes: map[string]attr.Type{
"availability_zone": types.StringType,
"instance_type": types.StringType,
"engine_version": types.StringType,
},
}
nodeAttrValues := make([]attr.Value, 0, len(nodeObjects))
for _, node := range nodeObjects {
objVal, diags := types.ObjectValueFrom(ctx, nodeType.AttrTypes, node)
resp.Diagnostics.Append(diags...)
nodeAttrValues = append(nodeAttrValues, objVal)
}
state.Nodes, diags = types.ListValue(nodeType, nodeAttrValues)
resp.Diagnostics.Append(diags...)
Best Practices
1. Appropriate Nesting
// ✅ Good: Logical grouping
network_config = {
vpc_id = "..."
subnet_ids = [...]
}
// ❌ Bad: Excessive nesting
config = {
network = {
vpc = {
id = {
value = "..."
}
}
}
}
2. Clear Structure
// ✅ Good: Intuitive structure
nodes = [
{
availability_zone = "us-west-2a"
instance_type = "cache.t3.micro"
}
]
// ❌ Bad: Confusing structure
nodes = {
"0" = {
config = ["us-west-2a", "cache.t3.micro"]
}
}
3. Type Selection
// ✅ List: Ordered items
ordered_rules = [rule1, rule2, rule3]
// ✅ Set: Unordered, unique items
security_groups = [sg1, sg2]
// ✅ Map: Key-value associations
tags = {
Environment = "prod"
Team = "platform"
}
Key Takeaways
- ✅ Use nested attributes for complex structures
- ✅ Choose appropriate collection types (list/set/map)
- ✅ Keep nesting depth reasonable (2-3 levels)
- ✅ Implement cross-attribute validation
- ✅ Provide clear documentation
- ✅ Handle null/unknown values properly
- ✅ Build complex values methodically
- ✅ Test complex attribute scenarios
What’s Next?
Learn performance optimization techniques for provider efficiency.
Next: Performance Optimization →
Previous: ← Provider-Defined Functions
Next: Performance Optimization →
Last updated: July 1, 2026