Resource Lifecycle API Reference¶
Complete API reference for resource lifecycle methods and the ResourceContext object.
🤖 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.
Base Resource Class¶
| Python | |
|---|---|
Class Attributes¶
| Attribute | Type | Required | Description |
|---|---|---|---|
config_class |
Type[attrs.define] |
Yes | Configuration attrs class |
state_class |
Type[attrs.define] |
Yes | State attrs class |
Lifecycle Methods¶
Required Methods¶
read()¶
| Python | |
|---|---|
Purpose: Check if the resource still exists and return its current state.
Parameters:
- ctx: ResourceContext with current state
Returns:
- StateType: Current state if resource exists
- None: Resource no longer exists (deleted outside Terraform)
When Called: During terraform plan and terraform refresh
Example:
| Python | |
|---|---|
_delete_apply()¶
Purpose: Remove the resource from the remote system.
Parameters:
- ctx: ResourceContext with current state
Returns: None
When Called: During terraform destroy or when resource removed from config
Example:
| Python | |
|---|---|
Optional Methods¶
_create_apply()¶
| Python | |
|---|---|
Purpose: Create a new resource.
Parameters:
- ctx: ResourceContext with configuration
Returns:
- Tuple of (state, private_data)
- state: New state to track
- private_data: Sensitive data (not in state file)
When Called: During terraform apply when creating new resource
Default Behavior: Calls _update_apply() if not overridden
Example:
_update_apply()¶
| Python | |
|---|---|
Purpose: Modify an existing resource.
Parameters:
- ctx: ResourceContext with configuration and current state
Returns:
- Tuple of (state, private_data)
- state: Updated state
- private_data: Sensitive data (not in state file)
When Called: During terraform apply when updating existing resource
Example:
_validate_config()¶
| Python | |
|---|---|
Purpose: Validate user configuration before applying.
Parameters:
- config: Typed configuration object
Returns: - List of error messages (empty list = valid)
When Called: During terraform plan and terraform apply
Example:
| Python | |
|---|---|
ResourceContext API¶
Properties¶
config¶
| Python | |
|---|---|
Typed configuration attrs instance. None if configuration contains unknown values.
Example:
config_cty¶
| Python | |
|---|---|
Raw CTY value from Terraform, always available even with unknown values.
Example:
| Python | |
|---|---|
state¶
| Python | |
|---|---|
Current state. None during create operations.
Example:
planned_state¶
| Python | |
|---|---|
Planned state from plan phase.
Example:
| Python | |
|---|---|
Methods¶
is_field_unknown()¶
| Python | |
|---|---|
Check if a configuration field has an unknown value (e.g., depends on another resource).
Parameters:
- field_name: Name of the configuration field
Returns: True if value is unknown
Example:
| Python | |
|---|---|
add_error()¶
| Python | |
|---|---|
Add an error diagnostic to show in Terraform output.
Parameters:
- message: Error message
Example:
| Python | |
|---|---|
add_warning()¶
| Python | |
|---|---|
Add a warning diagnostic to show in Terraform output.
Parameters:
- message: Warning message
Example:
| Python | |
|---|---|
require_replace()¶
| Python | |
|---|---|
Force Terraform to destroy and recreate the resource instead of updating it in
place. Use this from _update() when replacement depends on the values
themselves; for "any change to this attribute forces replacement", prefer the
declarative requires_replace=True schema flag below.
Parameters:
- attribute_path: Attribute path that forces replacement, e.g. "size_gb" or "disks[0].type". An empty or whitespace-only path raises ValueError, so a typo cannot masquerade as a working call. Surrounding whitespace is stripped, so " size_gb " records "size_gb" rather than a path that matches nothing.
Notes:
- Calling it during a create is a no-op: a resource with no prior state cannot be replaced.
- Repeated calls with the same path are de-duplicated.
- _update() is a plan-time hook, so it still runs on a plan that ends in
replacement; it is the apply-time _update_apply() that Terraform skips in
favour of _delete_apply() then _create_apply().
Example:
| Python | |
|---|---|
Schema Definition¶
get_schema()¶
Returns: PvsSchema object defining the resource schema
Example:
Forcing Replacement (requires_replace)¶
Some attributes cannot be changed on an existing remote object. Mark them
requires_replace=True and Terraform plans a destroy-and-create whenever the
planned value differs from the value in state — the equivalent of the SDK's
ForceNew and the plugin framework's RequiresReplace() plan modifier.
| Python | |
|---|---|
Behaviour:
- Reported to Terraform in PlanResourceChange.Response.requires_replace. The
plan-time _update() hook still runs — replacement paths are collected from
its result — but Terraform then applies the change as a destroy-and-create, so
the apply-time _update_apply() is never called; _delete_apply() and
_create_apply() run instead.
- Never reported on create (no prior state) or on destroy (no planned state).
- A planned value that is still unknown counts as a change, since the plan has
to be decided before the value is resolved.
- Not valid on a computed-only attribute — the practitioner cannot change what
they cannot set, so a schema that does this raises ValueError. Use
optional=True, computed=True if the attribute is both settable and computed.
- Not valid on a write-only attribute either. Terraform requires write-only
values to be null in both prior and planned state, so the comparison would
always see null == null and replacement would silently never fire;
a schema that combines the two raises ValueError. Terraform's own SDK
rejects the same pairing (WriteOnly cannot be set with ForceNew). To rotate
a write-only secret, pair it with a companion attribute the practitioner
bumps — conventionally <name>_wo_version — and put requires_replace=True
on that, or call ctx.require_replace() from the plan hook.
- Only top-level attributes are compared. Inside a nested block or an
object-typed attribute the flag is unreachable — an attribute inside a block
has no stable path until Terraform matches the block's elements between prior
and planned state — so a schema that sets it there raises ValueError.
Promote the attribute to the top level, or state the path with
ctx.require_replace("disks[0].type") from the plan hook, which knows which
element changed.
- On optional=True, computed=True, replacement fires if the planned value is
unknown. The normal path carries prior state forward, so the value stays known
and nothing happens; but a plan hook that deliberately leaves the attribute
unknown gets a replacement on every plan. Leave it unknown only when the
attribute genuinely may change, or set it to the prior value when it will not.
For conditional replacement — replace only when a value shrinks, crosses a
boundary, or the remote API cannot perform the update — use
ctx.require_replace() from _update() instead.
Type Signatures¶
Configuration Class¶
| Python | |
|---|---|
State Class¶
| Python | |
|---|---|
Private Data¶
Sensitive data that shouldn't be stored in state:
Access private data:
| Python | |
|---|---|
Lifecycle Sequence¶
Create Resource¶
- User adds resource to Terraform config
terraform plan:- Calls
_validate_config()with configuration - Calls
get_schema()to validate schema terraform apply:- Calls
_create_apply()with ctx.config - Stores returned state
Update Resource¶
- User modifies resource in Terraform config
terraform plan:- Calls
read()to get current state - Calls
_validate_config()with new configuration - Compares current state with planned state
terraform apply:- Calls
_update_apply()with ctx.config and ctx.state - Stores returned state
Delete Resource¶
- User removes resource from Terraform config
terraform plan:- Shows resource will be deleted
terraform apply:- Calls
_delete_apply()with ctx.state - Removes resource from state
Refresh State¶
terraform refreshorterraform plan:- Calls
read()with ctx.state - Updates state if changed
- Removes from state if
read()returnsNone
See Also¶
- Create a Resource - How-to guide
- Building Your First Resource - Tutorial
- Add Validation - Validation patterns
- API Reference - Auto-generated API docs