Skip to content

Computed Attributes

Computed attributes are values that are calculated or generated by the provider rather than provided by the user. They represent outputs from resource operations.

🤖 AI-Generated Content

This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.

What are Computed Attributes?

Computed attributes: - Are set by the provider (not by Terraform users) - Represent outputs (IDs, timestamps, derived values) - Cannot be configured by users in Terraform - May be unknown during planning and only determined during apply

Basic Example

Define computed attributes with computed=True:

Python
from pyvider.schema import s_resource, a_str, a_num, PvsSchema

@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        # User inputs (required or with defaults)
        "name": a_str(required=True, description="Server name"),

        # Provider outputs (computed=True)
        "id": a_str(computed=True, description="Unique server ID"),
        "ip_address": a_str(computed=True, description="Assigned IP address"),
        "created_at": a_str(computed=True, description="Creation timestamp"),
        "status": a_str(computed=True, description="Current status"),
    })

Setting Computed Values

Set computed attributes in your resource lifecycle methods:

Python
from pyvider.resources import register_resource, BaseResource
from pyvider.resources.context import ResourceContext
from pyvider.schema import s_resource, a_str, a_num, PvsSchema
import attrs
from datetime import datetime
import uuid

@attrs.define
class ServerConfig:
    name: str
    instance_type: str = "t2.micro"

@attrs.define
class ServerState:
    id: str
    name: str
    instance_type: str
    ip_address: str
    created_at: str
    status: str

@register_resource("server")
class Server(BaseResource):
    config_class = ServerConfig
    state_class = ServerState

    @classmethod
    def get_schema(cls) -> PvsSchema:
        return s_resource({
            # Inputs
            "name": a_str(required=True, description="Server name"),
            "instance_type": a_str(default="t2.micro", description="Instance type"),

            # Computed outputs
            "id": a_str(computed=True, description="Server ID"),
            "ip_address": a_str(computed=True, description="IP address"),
            "created_at": a_str(computed=True, description="Creation time"),
            "status": a_str(computed=True, description="Server status"),
        })

    async def _create_apply(self, ctx: ResourceContext) -> tuple[ServerState | None, None]:
        """Create server and set computed values."""
        if not ctx.config:
            return None, None

        # Generate computed values
        server_id = str(uuid.uuid4())
        ip_address = self._allocate_ip()
        created_at = datetime.utcnow().isoformat()

        # Call API to create server
        await self.api.create_server(
            id=server_id,
            name=ctx.config.name,
            instance_type=ctx.config.instance_type,
        )

        # Return state with computed values
        return ServerState(
            id=server_id,
            name=ctx.config.name,
            instance_type=ctx.config.instance_type,
            ip_address=ip_address,
            created_at=created_at,
            status="running",
        ), None

    async def read(self, ctx: ResourceContext) -> ServerState | None:
        """Refresh computed values from API."""
        if not ctx.state:
            return None

        # Fetch current state from API
        server = await self.api.get_server(ctx.state.id)

        if not server:
            return None  # Server deleted

        # Update computed values from API
        return ServerState(
            id=ctx.state.id,
            name=server.name,
            instance_type=server.instance_type,
            ip_address=server.ip_address,
            created_at=ctx.state.created_at,
            status=server.status,  # May have changed
        )

Common Computed Attribute Patterns

IDs and Identifiers

Generate unique IDs for resources:

Python
@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        "name": a_str(required=True),

        # Computed IDs
        "id": a_str(computed=True, description="Unique resource ID"),
        "arn": a_str(computed=True, description="Amazon Resource Name"),
        "resource_id": a_str(computed=True, description="External system ID"),
    })

async def _create_apply(self, ctx: ResourceContext):
    # Generate or receive ID from API
    resource_id = await self.api.create(ctx.config.name)

    return State(
        id=resource_id,
        arn=f"arn:aws:service:region:account:{resource_id}",
        resource_id=resource_id,
        name=ctx.config.name,
    ), None

Timestamps

Track creation and modification times:

Python
from datetime import datetime

@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        "name": a_str(required=True),

        # Timestamp fields
        "created_at": a_str(computed=True, description="Creation timestamp"),
        "updated_at": a_str(computed=True, description="Last update timestamp"),
    })

async def _create_apply(self, ctx: ResourceContext):
    now = datetime.utcnow().isoformat()

    return State(
        id=generate_id(),
        name=ctx.config.name,
        created_at=now,
        updated_at=now,
    ), None

async def _update_apply(self, ctx: ResourceContext):
    now = datetime.utcnow().isoformat()

    return State(
        id=ctx.state.id,
        name=ctx.config.name,
        created_at=ctx.state.created_at,  # Preserve original
        updated_at=now,  # Update timestamp
    ), None

Network Information

Compute network-related values:

Python
@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        "name": a_str(required=True),

        # Network information
        "ip_address": a_str(computed=True, description="Assigned IP"),
        "public_dns": a_str(computed=True, description="Public DNS name"),
        "private_dns": a_str(computed=True, description="Private DNS name"),
        "mac_address": a_str(computed=True, description="MAC address"),
    })

async def _create_apply(self, ctx: ResourceContext):
    # Get network info from API
    network_info = await self.api.allocate_network(ctx.config.name)

    return State(
        id=network_info.id,
        name=ctx.config.name,
        ip_address=network_info.ip,
        public_dns=network_info.public_dns,
        private_dns=network_info.private_dns,
        mac_address=network_info.mac,
    ), None

Derived Values

Calculate values based on other attributes:

Python
@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        "size_gb": a_num(required=True, description="Size in GB"),

        # Derived computed values
        "size_bytes": a_num(computed=True, description="Size in bytes"),
        "size_mb": a_num(computed=True, description="Size in MB"),
    })

async def _create_apply(self, ctx: ResourceContext):
    size_gb = ctx.config.size_gb

    return State(
        id=generate_id(),
        size_gb=size_gb,
        size_bytes=size_gb * 1024 * 1024 * 1024,
        size_mb=size_gb * 1024,
    ), None

Status and State

Track resource status:

Python
@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        "name": a_str(required=True),

        # Status fields
        "status": a_str(computed=True, description="Resource status"),
        "ready": a_bool(computed=True, description="Whether resource is ready"),
        "health": a_str(computed=True, description="Health check status"),
    })

async def read(self, ctx: ResourceContext):
    # Check current status
    health_check = await self.api.health_check(ctx.state.id)

    return State(
        id=ctx.state.id,
        name=ctx.state.name,
        status=health_check.status,
        ready=health_check.status == "healthy",
        health=health_check.message,
    )

Optional + Computed

Attributes can be both optional (user can provide) and computed (provider can calculate):

Python
@classmethod
def get_schema(cls) -> PvsSchema:
    return s_resource({
        "name": a_str(required=True),

        # Optional + Computed: User can provide, or provider will generate
        "tags": a_map(a_str(), optional=True, computed=True, description="Resource tags"),
    })

async def _create_apply(self, ctx: ResourceContext):
    # Use user-provided tags, or generate defaults
    tags = ctx.config.tags if ctx.config.tags else {
        "managed_by": "terraform",
        "created_at": datetime.utcnow().isoformat(),
    }

    return State(
        id=generate_id(),
        name=ctx.config.name,
        tags=tags,
    ), None

Using Computed Attributes in Terraform

Users reference computed attributes in their configurations:

Terraform
resource "mycloud_server" "web" {
  name          = "web-server"
  instance_type = "t2.micro"
}

# Use computed outputs
output "server_id" {
  value = mycloud_server.web.id
}

output "server_ip" {
  value = mycloud_server.web.ip_address
}

# Use computed values in other resources
resource "mycloud_dns_record" "web" {
  name  = "web.example.com"
  value = mycloud_server.web.ip_address  # Computed from server
  type  = "A"
}

# Conditional logic based on computed values
resource "mycloud_alert" "server_health" {
  server_id = mycloud_server.web.id
  enabled   = mycloud_server.web.status == "running"
}

Best Practices

1. Always Mark Outputs as Computed

Python
1
2
3
4
5
# ✅ Good: Output clearly marked as computed
"id": a_str(computed=True, description="Resource ID")

# ❌ Bad: Output not marked (will confuse users)
"id": a_str(description="Resource ID")

2. Preserve Immutable Computed Values

Python
1
2
3
4
5
6
async def _update_apply(self, ctx: ResourceContext):
    return State(
        id=ctx.state.id,  # ✅ Preserve ID
        created_at=ctx.state.created_at,  # ✅ Preserve creation time
        updated_at=datetime.utcnow().isoformat(),  # ✅ Update timestamp
    )

3. Handle Unknown Values During Planning

Python
1
2
3
4
5
6
7
8
async def _create(self, ctx: ResourceContext, base_plan: dict):
    """Planning phase: Values may be unknown."""

    # Mark fields that will be known after apply
    base_plan["id"] = None  # Will be generated
    base_plan["ip_address"] = None  # Will be allocated

    return base_plan, None

4. Document What is Computed

Python
1
2
3
4
"status": a_str(
    computed=True,
    description="Current server status (healthy, degraded, or offline)"
)

5. Make Computed Values Stable

Python
# ✅ Good: Stable, deterministic
"endpoint": a_str(computed=True)

async def _create_apply(self, ctx):
    # Endpoint is deterministic based on inputs
    endpoint = f"https://{ctx.config.name}.api.example.com"
    return State(endpoint=endpoint), None

# ❌ Bad: Changes on every apply
async def _create_apply(self, ctx):
    # Random value changes every time
    endpoint = f"https://{uuid.uuid4()}.api.example.com"
    return State(endpoint=endpoint), None

See Also