Conversion functions

The pure helpers exported by nix-tf-schema, one section per file, rendered from their doc-comments. Every function exercised by the conversion-test.nix suite is documented here.

Terraform schema conversion (conversion.nix)

Terraform conversion helpers: translate between Terraform provider schemas and Nix options.

tf-schema.conversion.withContext

Tag a value with an error-trace breadcrumb, so a failure deep in a schema walk says which resource, attribute or block it was walking.

The message is prefixed with [nix-tf-schema], which is what makes the breadcrumbs greppable out of an otherwise enormous --show-trace:

nix ... --show-trace 2> >(grep "… \[nix-tf-schema\]")

builtins.addErrorContext covers a value's evaluation only as far as weak head normal form, so wrapping a whole tree once would catch almost nothing. The walks below therefore tag each recursion step -- the point where the attribute or block name is still in scope -- and the trace reads back as the path, innermost first.

Inputs

what
What was being done, as a phrase completing "while ..." (e.g. "reading attribute host").
value
The value to evaluate under that context.

Type

withContext :: String -> a -> a

Example

withContext "converting resource `netbox_cluster`" (resourceToOptionTypes schema)
=> <the options, or a failure whose trace names the resource>

tf-schema.conversion.wrapTfType

Fold a (possibly nested) Terraform type spec into its string form, e.g. [ "list" "string" ] becomes "list(string)".

Inputs

tfType
A Terraform type spec: either a bare type string, or a (possibly nested) list of type constructors innermost-last.

Type

wrapTfType :: (List | String) -> String

Example

wrapTfType [ "list" "string" ]
=> "list(string)"
wrapTfType "string"
=> "string"

tf-schema.conversion.wrapTfAttr

Normalize a single Terraform attribute schema into a tfvars variable declaration: drop schema-only keys, wrap the type, and default optionals to null.

Inputs

attr
A single Terraform attribute schema (with a type and the boolean schema flags such as optional/computed).

Type

wrapTfAttr :: AttrSet -> AttrSet

Example

wrapTfAttr { type = [ "list" "string" ]; optional = true; description = "hosts"; }
=> { type = "list(string)"; description = "hosts"; default = null; }

tf-schema.conversion.wrapTfAttrs

Wrap a set of Terraform attributes into a tfvars variable block, excluding purely-computed attributes.

Inputs

tfAttrs
Attribute set of Terraform attribute schemas, keyed by attribute name.

Type

wrapTfAttrs :: AttrSet -> AttrSet

Example

wrapTfAttrs {
  host = { type = "string"; };
  id = { type = "string"; computed = true; };
}
=> { variable = { host = { type = "string"; }; }; }   # `id` dropped (computed)

tf-schema.conversion.wrapTfSourceSchemas

Wrap each source schema's block attributes via wrapTfAttrs.

Inputs

sourceSchemas
Attribute set of Terraform source schemas (each with a block.attributes), keyed by data-source or resource name.

Type

wrapTfSourceSchemas :: AttrSet -> AttrSet

Example

wrapTfSourceSchemas {
  incus_instance = { block.attributes = { name = { type = "string"; }; }; };
}
=> { incus_instance = { variable = { name = { type = "string"; }; }; }; }

tf-schema.conversion.wrapTfProvider

Wrap a provider schema's data-source and resource schemas.

Inputs

schema
A single provider's schema, as found under provider_schemas.<provider> in tofu providers schema -json output.

Type

wrapTfProvider :: AttrSet -> AttrSet

Example

wrapTfProvider {
  resource_schemas.incus_instance = { block.attributes = { name = { type = "string"; }; }; };
  data_source_schemas = { };
}
=> {
     resource_schemas.incus_instance = { variable = { name = { type = "string"; }; }; };
     data_source_schemas = { };
   }

tf-schema.conversion.wrapTfProviderSchema

Wrap a full tofu providers schema -json output, stripping the registry.opentofu.org/ prefix from provider keys.

Inputs

output
The parsed tofu providers schema -json output (an attribute set with a provider_schemas field keyed by fully-qualified provider address).

Type

wrapTfProviderSchema :: AttrSet -> AttrSet

Example

wrapTfProviderSchema {
  provider_schemas."registry.opentofu.org/lxc/incus" = { resource_schemas = { }; data_source_schemas = { }; };
}
=> { "lxc/incus" = { resource_schemas = { }; data_source_schemas = { }; }; }

tf-schema.conversion.tfAttrType

Module type validating a single Terraform attribute schema (its type, description, and the boolean schema flags such as computed/optional).

Type

tfAttrType :: ModuleType

tf-schema.conversion.fromTfTypes

Convert a (possibly nested) Terraform type spec to the corresponding Nix module type, e.g. [ "list" "string" ] to listOf str.

Inputs

tfTypes
A flattened Terraform type spec as a list of constructors, outermost first (e.g. [ "list" "string" ]).

Type

fromTfTypes :: List -> ModuleType

Example

fromTfTypes [ "list" "string" ]
=> listOf str   # the nixpkgs option type

tf-schema.conversion.fromTfVar

Convert a single (validated) Terraform attribute to a Nix mkOption. Throws for computed attributes, which cannot be set from Nix.

Inputs

tfAttr
A single Terraform attribute schema (validated against tfAttrType): its type, description, and the computed/optional/required flags.

Type

fromTfVar :: AttrSet -> Option

Example

fromTfVar { type = "string"; description = "host"; optional = true; }
=> lib.mkOption { type = nullOr str; default = null; description = "host"; }

tf-schema.conversion.fromTfVars

Convert a set of Terraform attributes to a submodule type, skipping computed and deprecated attributes (and sensitive ones unless allowSensitive).

Inputs

allowSensitive
Whether to keep attributes flagged sensitive.
tfAttrs
Attribute set of Terraform attribute schemas, keyed by attribute name.

Type

fromTfVars :: Bool -> AttrSet -> ModuleType

Example

fromTfVars false {
  host = { type = "string"; optional = true; description = "host"; };
  id = { type = "string"; computed = true; };
}
=> submodule { options.host = mkOption { type = nullOr str; default = null; }; }
   # `id` dropped (computed)

tf-schema.conversion.fromTfSourceSchemas

Convert a set of source schemas to a submodule whose options are each source's variables.

Inputs

allowSensitive
Whether to keep attributes flagged sensitive.
schemas
Attribute set of Terraform source schemas (each with block.attributes), keyed by data-source or resource name.

Type

fromTfSourceSchemas :: Bool -> AttrSet -> ModuleType

Example

fromTfSourceSchemas false {
  incus_instance = { block.attributes.name = { type = "string"; required = true; description = "name"; }; };
}
=> submodule { options.incus_instance = mkOption { type = submodule { ... }; }; }
   # the inner submodule carries a `name` option

tf-schema.conversion.fromTfProvider

Convert a provider schema to a submodule covering its data-source and resource schemas.

Inputs

allowSensitive
Whether to keep attributes flagged sensitive.
schema
A single provider's schema (with resource_schemas/data_source_schemas).

Type

fromTfProvider :: Bool -> AttrSet -> ModuleType

Example

fromTfProvider false {
  resource_schemas.incus_instance = { block.attributes.name = { type = "string"; required = true; description = "name"; }; };
  data_source_schemas = { };
}
=> submodule {
     options.resource_schemas = mkOption { ... };     # one submodule per resource
     options.data_source_schemas = mkOption { ... };
   }

tf-schema.conversion.fromTfProviderSchema

Convert a full tofu providers schema -json output to a submodule keyed by provider, stripping the registry.opentofu.org/ prefix.

Inputs

allowSensitive
Whether to keep attributes flagged sensitive.
output
The parsed tofu providers schema -json output (with provider_schemas).

Type

fromTfProviderSchema :: Bool -> AttrSet -> ModuleType

Example

fromTfProviderSchema false {
  provider_schemas."registry.opentofu.org/lxc/incus" = { resource_schemas = { }; data_source_schemas = { }; };
}
=> submodule { options."lxc/incus" = mkOption { ... }; }   # one option per provider

tf-schema.conversion.mkTfModuleJson

Generate a complete Terraform module as JSON with variable blocks and var.* references. Values flow through TF_VAR_* env vars at runtime, keeping runtime override capability.

Inputs

spec
An attribute set describing the module: providers (required-provider blocks), variables (a list of names or a pre-built variable set), optional data, and resource/output blocks.

Type

mkTfModuleJson :: AttrSet -> AttrSet

Example

mkTfModuleJson {
  providers = { netbox = { source = "e-breuninger/netbox"; version = "= 5.0.0"; }; };
  variables = [ "name" "slug" ];
  resource = { netbox_cluster_type.type = { name = "\${var.name}"; }; };
  output = { id.value = "\${netbox_cluster_type.type.id}"; };
}
=> {
     terraform.required_providers.netbox = { source = "e-breuninger/netbox"; version = "= 5.0.0"; };
     terraform.backend.http = { };
     variable = { name = { }; slug = { }; };
     resource.netbox_cluster_type.type.name = "\${var.name}";
     output.id.value = "\${netbox_cluster_type.type.id}";
   }

tf-schema.conversion.unbrace

Strip the ${ ... } splice from a Terraform reference expression, so it can be embedded inside a larger one.

Anything that is not spliced is returned unchanged, so this is safe to apply to a literal.

Inputs

value
A reference (or anything with a __toString), or a plain string.

Type

unbrace :: (String | Reference) -> String

Example

unbrace (ref.var "cluster_id")
=> "var.cluster_id"

unbrace "plain"
=> "plain"

tf-schema.conversion.referenceType

A module type for a Terraform reference expression: a string spliced as ${...}, which Terraform evaluates rather than takes literally.

It accepts both a bare string written by hand and a ref chain (an attrset carrying __toString), and merges to the rendered string, so a definition can be handed straight to builtins.toJSON.

Warning

This is an authoring primitive for the parts of a Terraform module that are written by hand -- data, output, extra resource attributes -- not for schema-derived options. Those are shipped to Terraform as TF_VAR_* environment variables, and Terraform reads a variable's value as a literal: a ${...} placed in one lands in the plan verbatim and is never interpolated. Schema-derived types are deliberately not widened to accept references (which is where this departs from tofunix, whose values are baked into the module JSON and so can hold references anywhere).

Type

referenceType :: OptionType

Example

cast (attrsOf referenceType) { id = ref.var "cluster_id"; }
=> { id = "\${var.cluster_id}"; }

tf-schema.conversion.refOr

Widen a type to also accept a Terraform reference expression.

Use it on hand-written module JSON, where a value may equally be a literal or a pointer at another resource. See referenceType for why schema-derived options are not widened this way.

Inputs

type
The literal type to widen.

Type

refOr :: OptionType -> OptionType

Example

cast (refOr int) 3
=> 3

cast (refOr int) (ref.var "count")
=> "\${var.count}"

tf-schema.conversion.ref

Build a Terraform reference expression by application, one path segment at a time: ref "netbox_cluster" "main" "id" is "\${netbox_cluster.main.id}".

The result is a chain that is simultaneously callable and renderable, so there is no terminator to remember -- every prefix of a chain is itself a valid reference. Terraform's own top-level scopes (var, local, data, module, each, count, self, path, terraform) are pre-seeded as attributes, so they read as ref.var "cluster_id".

A segment that is an int, another reference, or "*" renders as a bracket index rather than a dotted attribute.

Nothing here consults a schema: a reference is only checked by Terraform, at plan time.

Type

ref :: String -> Reference

Example

toString (ref "netbox_cluster" "main" "id")
=> "\${netbox_cluster.main.id}"

toString (ref.data "netbox_cluster" "main" "id")
=> "\${data.netbox_cluster.main.id}"

toString (ref "netbox_prefix" "pool" "*" "prefix")
=> "\${netbox_prefix.pool[*].prefix}"

toString (ref "netbox_prefix" "pool" (ref.count "index") "prefix")
=> "\${netbox_prefix.pool[count.index].prefix}"

tf-schema.conversion.resourceAttrsToOptions

Convert the settable attributes of a Terraform resource schema to Nix options.

Inputs

tfAttrs
Attribute set of Terraform resource attribute schemas, keyed by attribute name. Purely-computed attributes are skipped.

Type

resourceAttrsToOptions :: AttrSet -> AttrSet

Example

resourceAttrsToOptions {
  name = { type = "string"; required = true; description = "name"; };
  id = { type = "string"; computed = true; };
}
=> { name = mkOption { type = str; description = "name"; }; }   # `id` dropped (computed)

tf-schema.conversion.resourceAttrsToVarRefs

Generate ${var.<name>} references for every settable attribute of a Terraform resource schema.

Inputs

tfAttrs
Attribute set of Terraform resource attribute schemas, keyed by attribute name. Purely-computed attributes are skipped.

Type

resourceAttrsToVarRefs :: AttrSet -> AttrSet

Example

resourceAttrsToVarRefs { name = { type = "string"; required = true; }; }
=> { name = "\${var.name}"; }

tf-schema.conversion.settableTree

Recursively describe the user-settable surface of one resource schema as a tree of nodes, covering both schema dialects a provider may speak.

The dialects differ in exactly three places, and agree everywhere else -- which is why one walk handles both rather than two parallel readers:

terraform-plugin-sdk/v2 terraform-plugin-framework
nested objects live in block.block_types.<k> block.attributes.<k>.nested_type
"required" is spelled min_items >= 1 required = true on the attribute
a singleton is a list/set with max_items == 1, still encoded as a one-element list nesting_mode = "single", encoded as a plain object

The singleBlock flag records that third row: it is true only for the sdk/v2 case, and it is the one fact a renderer cannot recover from the Nix type alone.

blockSyntax records the first row, which a renderer needs for the same reason: an sdk/v2 nested object is a block, which configuration syntax cannot fill from a variable and a generator must therefore emit a dynamic block for, while a plugin-framework one is an ordinary attribute whose value happens to be an object -- assignable directly. The nesting alone does not tell them apart: a plural sdk/v2 block and a framework list-of-objects attribute both read list.

The two are not alternatives to pick between per provider -- a single resource can carry both, as incus_instance does (sdk/v2 device/file blocks alongside a framework exec attribute), so both readers recurse into the same plain-attribute reader.

Purely-computed attributes (those computed without optional) are dropped, matching settableAttrs.

Every node carries the same key set, so consumers need not branch on dialect: path (the dotted path from the resource's root, which is what the generators name in their error breadcrumbs), kind ("attr" or "block"), tfType (the Terraform type spec, null for blocks), required, optional, sensitive, description, nesting (null for plain attributes), singleBlock, and blockSyntax. block nodes additionally carry children, a nested tree of the same shape.

Inputs

resourceSchema
One resource schema, as found under provider_schemas.<provider>.resource_schemas.<resource> in tofu providers schema -json output.

Type

settableTree :: AttrSet -> AttrSet

Example

settableTree {
  block = {
    attributes.name = { type = "string"; required = true; };
    block_types.smtp_server = {
      nesting_mode = "list";
      max_items = 1;
      block.attributes.host = { type = "string"; required = true; };
    };
  };
}
=> {
     name = { path = "name"; kind = "attr"; tfType = "string"; required = true; ... };
     smtp_server = {
       path = "smtp_server"; kind = "block"; nesting = "list"; singleBlock = true;
       children.host = { path = "smtp_server.host"; kind = "attr"; tfType = "string"; ... };
       ...
     };
   }

tf-schema.conversion.settablePaths

Flatten settableTree into a map from dotted path to node, dropping the children key. Nested blocks appear both as a path of their own and via their children's paths, so the result is a complete inventory of what a user may set on a resource -- the input to schema-drift checks and to per-path overrides.

Inputs

resourceSchema
One resource schema (see settableTree).

Type

settablePaths :: AttrSet -> AttrSet

Example

settablePaths {
  block.block_types.smtp_server = {
    nesting_mode = "list";
    max_items = 1;
    block.attributes.host = { type = "string"; required = true; };
  };
}
=> {
     "smtp_server" = { kind = "block"; singleBlock = true; ... };
     "smtp_server.host" = { kind = "attr"; tfType = "string"; ... };
   }

tf-schema.conversion.nodeToOptionType

Convert one settableTree node to a Nix module type. Plain attributes go through fromTfTypes; nested blocks become a submodule built recursively from their children, wrapped per nesting mode:

nesting Nix type
single / group submodule
list / set listOf (submodule ...)
map attrsOf (submodule ...)

A singleBlock node is typed as a bare submodule: the user writes one object, and it is the renderer's job to encode it as the one-element list Terraform expects.

Inputs

node
One node of a settableTree.

Type

nodeToOptionType :: AttrSet -> ModuleType

Example

nodeToOptionType { kind = "attr"; tfType = [ "list" "string" ]; }
=> listOf str

tf-schema.conversion.nodeToOption

Convert one settableTree node to a Nix option: required nodes get the bare type, optional ones a nullOr defaulting to null, so an unset option is distinguishable from an empty one and can be dropped from the generated Terraform config.

Note

There is deliberately no way to say "explicitly null" as distinct from "unset". Terraform does not need one for what this generates: a resource argument set to null behaves exactly as if it had been omitted. The optional(...) constraints tfTypeConstraint renders carry no default -- a provider schema has none to give -- so the two coincide inside object types too. tofunix carries an unset sentinel because it bakes literals into the module JSON, where an unset option would otherwise emit a null key into blocks that reject one; here an unset value is simply absent from the environment and its variable falls back to null. Revisit if a code path ever renders optional(T, default), or needs a key absent from a JSON payload rather than present-and-null.

Inputs

node
One node of a settableTree.

Type

nodeToOption :: AttrSet -> Option

Example

nodeToOption { kind = "attr"; tfType = "string"; optional = true; description = "host"; }
=> lib.mkOption { type = nullOr str; default = null; description = "host"; }

tf-schema.conversion.resourceToOptionTypes

Convert a whole resource schema to Nix options, one per settable top-level attribute or nested block, recursing through both schema dialects. The schema-driven counterpart of a hand-written option set.

Inputs

resourceSchema
One resource schema (see settableTree).

Type

resourceToOptionTypes :: AttrSet -> AttrSet

Example

resourceToOptionTypes {
  block.attributes = {
    name = { type = "string"; required = true; };
    id = { type = "string"; computed = true; };
  };
}
=> { name = mkOption { type = str; }; }   # `id` dropped (computed)

tf-schema.conversion.tfTypeConstraint

Render a Terraform type spec as the HCL type-constraint expression a variable block takes. The structural counterpart of wrapTfType, which only handles the collection constructors; this one also carries the structural types (object, tuple) a schema may nest inside them.

An object spec may carry a third element listing the attributes that are optional; those are wrapped in optional(...), without which Terraform would read them as required and reject a value that omits them.

Inputs

tfType
A Terraform type spec: a bare type string, or a constructor list such as [ "list" "string" ] or [ "object" { a = "string"; } [ "a" ] ].

Type

tfTypeConstraint :: (String | List) -> String

Example

tfTypeConstraint [ "map" [ "list" "string" ] ]
=> "map(list(string))"

tf-schema.conversion.nodeToTfTypeConstraint

Render one settableTree node as the HCL type constraint of the variable carrying it. Plain attributes come from their Terraform type spec; a block becomes an object over its children, with optional(...) around every non-required one, wrapped per nesting mode:

nesting constraint
singleton / single / group object({ ... })
list / set list(object({ ... }))
map map(object({ ... }))

A set block is typed as a list, matching nodeToOptionType's listOf: Nix has no unordered collection to feed a set(...) from.

Inputs

node
One node of a settableTree.

Type

nodeToTfTypeConstraint :: AttrSet -> String

Example

nodeToTfTypeConstraint {
  kind = "block"; nesting = "set"; singleBlock = false;
  children.name = { kind = "attr"; tfType = "string"; required = true; };
}
=> "list(object({ name = string }))"

tf-schema.conversion.resourceToVariables

Convert a whole resource schema to Terraform variable blocks, one per settable top-level attribute or nested block -- the input side of the resource body resourceToResourceBody renders.

A non-required node gets default = null, so a caller that supplies no value for it leaves the variable unset rather than failing tofu on an unassigned variable -- and, for a block, renders as no block at all.

A type is emitted only for nested objects. A plain attribute stays untyped (Terraform's any), which is both what the values arriving through TF_VAR_* already validate as and what keeps the generated JSON identical to a hand-listed variable set. A nested object's type, by contrast, is load-bearing: without it Terraform reads the TF_VAR_* value as the string it arrives as, rather than the object a dynamic block iterates or an attribute takes.

Inputs

resourceSchema
One resource schema (see settableTree).

Type

resourceToVariables :: AttrSet -> AttrSet

Example

resourceToVariables {
  block.attributes = {
    name = { type = "string"; required = true; };
    slug = { type = "string"; optional = true; };
  };
}
=> { name = { }; slug = { default = null; }; }

tf-schema.conversion.resourceToResourceBody

Convert a whole resource schema to the body of a Terraform resource block, wiring every settable node to the variable resourceToVariables declares for it.

A plain attribute becomes a ${var.<name>} reference. So does a plugin-framework nested object: it is an attribute whose value happens to be an object, so one reference carries the whole of it.

An sdk/v2 nested object is a genuine block, which configuration syntax cannot fill from a variable at all, so it becomes a dynamic block over that variable. That also lets one rendering serve all three arities: an unset optional block iterates an empty list and so renders as absent, a singleton iterates a one-element list, and a plural block iterates the collection itself. A literal skeleton of ${var.x} leaves could express neither of the first two -- an unset block would still render as present-with-null-fields.

The iterator is named after the block type rather than left at each, so a block nested inside a block still names its own level unambiguously. A map-nested block takes its map key as a block label, which dynamic spells as an explicit labels.

Inputs

resourceSchema
One resource schema (see settableTree).

Type

resourceToResourceBody :: AttrSet -> AttrSet

Example

resourceToResourceBody {
  block = {
    attributes.name = { type = "string"; required = true; };
    block_types.domain = {
      nesting_mode = "set";
      block.attributes.name = { type = "string"; required = true; };
    };
  };
}
=> {
     name = "\${var.name}";
     dynamic.domain = {
       for_each = "\${var.domain == null ? [] : var.domain}";
       content.name = "\${domain.value.name}";
     };
   }

tf-schema.conversion.metaArgOptions

Options for Terraform's resource meta-arguments: the arguments every resource takes regardless of its schema, because they are handled by Terraform rather than by the provider.

Deliberately kept apart from resourceToOptionTypes: schema-derived options become TF_VAR_* variables, and none of these can be one. lifecycle, depends_on and provider may not reference a variable at all -- Terraform resolves them before variables are known -- and routing count/for_each through a variable would put a value in the environment that only ever describes the module's own shape. They render straight into the resource body instead, via metaArgsToResourceBody.

Every option defaults to null, meaning "do not emit".

Type

metaArgOptions :: AttrSet

Example

submodule { options = metaArgOptions; }
=> # a type accepting `{ count = 2; lifecycle.prevent_destroy = true; }`

tf-schema.conversion.metaArgsToResourceBody

Render meta-argument values into the fragment of a Terraform resource body they correspond to, dropping everything left unset.

Merge it over the schema-derived body from resourceToResourceBody; the two cannot collide, since Terraform reserves these names.

depends_on and lifecycle.replace_triggered_by hold bare references -- HCL spells them unspliced, and the JSON syntax follows -- so a ref handed to either is unbraced on the way out.

Inputs

meta
Values for (a subset of) metaArgOptions.

Type

metaArgsToResourceBody :: AttrSet -> AttrSet

Example

metaArgsToResourceBody {
  count = 2;
  depends_on = [ (ref "netbox_cluster" "main") ];
  lifecycle.prevent_destroy = true;
}
=> {
     count = 2;
     depends_on = [ "netbox_cluster.main" ];
     lifecycle.prevent_destroy = true;
   }

tf-schema.conversion.mkProviderSchemaFile

Build a normalized provider-schema JSON file in a sandboxed derivation, for vendoring into a consumer's source tree.

Unlike extractProviderSchemas this returns the file itself rather than importing it, so consumers commit the result and read it with builtins.fromJSON -- no import-from-derivation, and therefore no need to run a provider binary during evaluation (which a cross-architecture evaluation could not do anyway).

The output drops the data-source and provider-configuration blocks, keeps only resource_schemas, and injects the source and version that tofu providers schema -json does not itself report -- so the file is self-identifying and a consumer can assert it still matches its provider.

Inputs

provider
A packaged Terraform/OpenTofu provider, as accepted by pkgs.opentofu.withPlugins. Its version is used to pin and to stamp the output.
source
The provider's registry source address, e.g. "keycloak/keycloak".

Type

mkProviderSchemaFile :: { provider :: Derivation, source :: String } -> Derivation

Example

mkProviderSchemaFile { provider = pkgs.terraform-providers.keycloak_keycloak; source = "keycloak/keycloak"; }
=> <derivation provider-schema-keycloak-keycloak.json>

tf-schema.conversion.extractProviderSchemas

Extract provider schemas at build time by running tofu providers schema in a sandboxed derivation, returning the raw schema, the wrapped tfvars form, and the converted Nix module type.

Inputs

allowSensitive
Whether the converted module type keeps attributes flagged sensitive.
pluginFn
A plugin selector providers: [ ... ], as accepted by pkgs.opentofu.withPlugins, choosing which providers' schemas to extract.

Type

extractProviderSchemas :: Bool -> (Providers -> List) -> AttrSet

Example

extractProviderSchemas false (p: [ p.incus ])
=> {
     schema = { ... };      # raw `tofu providers schema -json` output
     wrapped = { ... };     # `tfvars` variable blocks
     converted = { ... };   # Nix submodule type for the provider
   }

Vendored lib helpers (lib.nix)

Minimal lib helpers vendored from Fediversity's core/lib/lib.nix so this repo has no dependency back on Fediversity's core/lib. Just the two functions the conversion library (and its test) need: evalOption and cast.

tf-schema.lib.evalOption

Evaluate a value against a NixOS option declaration, returning the resulting config value (with the option's defaults applied).

Inputs

opts
An option declaration (e.g. the result of lib.mkOption).
conf
The value to assign to that option.

Type

evalOption :: Option -> a -> b

Example

evalOption (lib.mkOption { type = lib.types.int; }) 3
=> 3

tf-schema.lib.cast

Evaluate a value against a NixOS option type, applying the type's defaults. A thin wrapper around evalOption that wraps the bare type in an option declaration for you.

Inputs

type
A NixOS option type (e.g. lib.types.int, or a submodule).
a
The value to evaluate against type.

Type

cast :: Type -> a -> b

Example

cast (lib.types.submodule { options.x = lib.mkOption { default = 1; }; }) { }
=> { x = 1; }   # the submodule's default applied