Dagger
Search

vm

and configuration using Terraform and Ansible, integrated with secure secret
management via Vault and SOPS.

This generated module was created with dagger init as a starting point for VM-related
operations. It demonstrates key DevOps tasks such as decrypting secrets, applying
Terraform infrastructure changes, generating dynamic Ansible inventories, and
executing Ansible playbooks to configure VMs. The module is designed to be flexible
and extensible to support your infrastructure automation needs.

The primary function Bake orchestrates this workflow, accepting Terraform directories,
encrypted files, Vault credentials, and Ansible parameters as inputs. It optionally
decrypts SOPS-encrypted configuration files before applying Terraform operations,
then parses Terraform outputs to generate inventory files for Ansible. It supports
multiple inventory types and allows you to specify Ansible playbooks and credentials.

BakeHarvester is the same workflow for the bootstrap case, where no control
plane exists yet to provision against: it renders a Harvester VM's manifests
from the harvester-vm KCL module, applies them straight through the
Kubernetes API, waits for the guest agent to report an IP, and runs Ansible
against it — no OpenTofu and no Crossplane involved.

This module can be invoked from the Dagger CLI or programmatically via the SDK,
making it suitable for integrating into CI/CD pipelines, GitOps workflows, or
custom operator/controller logic.

Future enhancements planned include:
- Rendering manifests or configs to branches/PRs for GitOps-style deployments
- Seamless integration with SOPS for secret management and decryption
- Advanced Terraform execution and output parsing features
- Enhanced Ansible inventory generation and execution customization
- VM testing and validation steps post-provisioning
- Automated merge requests/PR handling post-deployment

This documentation serves both as a high-level overview and a detailed guide
to the module’s capabilities and intended use cases.

Installation

dagger install github.com/stuttgart-things/blueprints/vm@v3.2.0

Entrypoint

Return Type
Vm
Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
func (m *MyModule) Example() *dagger.Vm  {
	return dag.
			Vm()
}
@function
def example() -> dagger.Vm:
	return (
		dag.vm()
	)
@func()
example(): Vm {
	return dag
		.vm()
}

Types

Vm 🔗

baseImage() 🔗

Return Type
String !
Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 base-image
func (m *MyModule) Example(ctx context.Context) string  {
	return dag.
			Vm().
			Baseimage(ctx)
}
@function
async def example() -> str:
	return await (
		dag.vm()
		.baseimage()
	)
@func()
async example(): Promise<string> {
	return dag
		.vm()
		.baseImage()
}

bakeFromGit() 🔗

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
gitRepositoryString !-

Repository to clone from GitHub

gitRefString "main"

Ref/Branch to checkout - If not specified, defaults to “main”

gitTokenSecret -

Github token for authentication (private repositories)

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 bake-from-git --git-repository string
func (m *MyModule) Example(gitRepository string) *dagger.Directory  {
	return dag.
			Vm().
			Bakefromgit(gitRepository)
}
@function
def example(gitrepository: str) -> dagger.Directory:
	return (
		dag.vm()
		.bakefromgit(gitrepository)
	)
@func()
example(gitRepository: string): Directory {
	return dag
		.vm()
		.bakeFromGit(gitRepository)
}

bakeHarvester() 🔗

BakeHarvester provisions a Harvester / KubeVirt VM straight through the Kubernetes API and then configures it with Ansible — no Crossplane, no OpenTofu, no control plane of any kind on the target side.

This is the bootstrap counterpart to BakeLocal: same shape (provision, read the machine’s address back, hand it to Ansible), but the provisioning step is “render manifests and apply them” instead of “terraform apply”. It exists for the chicken-and-egg case — the first VM on a Harvester cluster, the one that will go on to run the Crossplane management cluster that provisions every VM after it.

The pipeline:

  1. render PVC + cloud-init Secret + VirtualMachine from the harvester-vm KCL module (dagger/kcl)
  2. kubectl apply them against Harvester (dagger/kubernetes)
  3. poll the resulting VirtualMachineInstance until it is Running AND the guest agent has reported an IP
  4. run Ansible against that IP (dagger/ansible, via ExecuteAnsible)

The returned directory carries the rendered manifests, the generated inventory and outputs.json ({“vm_ips”: […]}) — the same output contract BakeLocal uses, so downstream consumers do not care which of the two provisioned the machine.

Note this is a one-shot, imperative provisioner: there is no reconcile loop and no drift correction. Updating or deleting the VM afterwards is kubectl’s job (or Crossplane’s, once the management cluster this VM bootstraps is up).

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
kubeConfigSecret !-

Kubeconfig for the Harvester cluster the VM is created on.

vmNameString !-

Name of the VM. Forced into the KCL render as vmName so the manifests and the VMI we poll for can never drift apart.

namespaceString "default"

Namespace for PVC, Secret and VirtualMachine. Forced into the KCL render as namespace for the same reason.

ociSourceString "ghcr.io/stuttgart-things/harvester-vm:0.2.0"

OCI reference of the harvester-vm KCL module.

kclParametersFileFile -

KCL parameters as a YAML file (imageId, storageClass, storage, cpuCores, memory, networkName, cloudInitSshKey, cloudInitPassword, …).

Prefer this over kclParameters for anything sensitive: the file is mounted into the render container, whereas –kcl-parameters values become operation arguments and are echoed by dagger --progress plain.

kclParametersString -

KCL parameters as comma-separated key=value pairs. Override the file. Do not put credentials here — see kclParametersFile.

encryptedFileFile -

SOPS-encrypted KCL parameters file. Decrypted in-memory and used instead of kclParametersFile; the plaintext never becomes an operation argument.

sopsKeySecret -

AGE key for decrypting encryptedFile.

skipNamespaceBoolean false

Skip creating the target namespace. By default BakeHarvester creates it (idempotently) first, because kubectl apply does not and a missing namespace is the most common way a bootstrap run dies on line one.

waitTimeoutInteger 900

Seconds to wait for the VM to report an IP. Generous by default: the guest has to boot, install/start qemu-guest-agent and get a DHCP lease.

waitIntervalInteger 15

Seconds between VMI polls.

ansiblePlaybooksString -

Ansible playbooks to run against the VM, comma-separated. Empty skips the Ansible stage entirely (render + apply + wait only).

ansibleRequirementsFileFile -No description provided
ansibleUserSecret -No description provided
ansiblePasswordSecret -No description provided
ansibleParametersString -No description provided
envSecretsSecret -

Extra environment for the Ansible container, as a secret in dotenv format (NAME=value per line), for playbooks using lookup(‘env’, …).

vaultRoleIdSecret -No description provided
vaultSecretIdSecret -No description provided
vaultUrlSecret -No description provided
vmiAppearTimeoutInteger 120

Seconds to wait for the VMI object to exist at all before giving up. See WaitForVmIp; 0 folds the check back into –wait-timeout.

ansibleWaitTimeoutInteger 30

Seconds to wait after the IP appears before Ansible connects. The agent reports an address slightly before sshd is reliably up.

requirementsTemplateString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements.yaml.tmpl"No description provided
requirementsDataString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements-data.yaml"No description provided
inventoryTypeString "simple"

Inventory type: “simple” (default [all] group) or “cluster”.

cacheBusterString ""

Any value that changes between runs — a timestamp, a CI run id. Forces a fresh fetch of the remote Ansible requirements instead of a cached render. The kubectl apply gets its own stamp regardless; see stampManifest.

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 bake-harvester --kube-config env:MYSECRET --vm-name string
func (m *MyModule) Example(kubeConfig *dagger.Secret, vmName string) *dagger.Directory  {
	return dag.
			Vm().
			Bakeharvester(kubeConfig, vmName)
}
@function
def example(kubeconfig: dagger.Secret, vmname: str) -> dagger.Directory:
	return (
		dag.vm()
		.bakeharvester(kubeconfig, vmname)
	)
@func()
example(kubeConfig: Secret, vmName: string): Directory {
	return dag
		.vm()
		.bakeHarvester(kubeConfig, vmName)
}

bakeLocal() 🔗

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
terraformDirDirectory !-No description provided
operationString "apply"No description provided
variablesString -

e.g., “cpu=4,ram=4096,storage=100”

encryptedFileFile -No description provided
sopsKeySecret -No description provided
awsAccessKeyIdSecret -No description provided
awsSecretAccessKeySecret -No description provided
vaultRoleIdSecret -No description provided
vaultSecretIdSecret -No description provided
vaultTokenSecret -

vaultToken

vaultUrlSecret -No description provided
ansiblePlaybooksString -No description provided
ansibleRequirementsFileFile -No description provided
ansibleUserSecret -No description provided
ansiblePasswordSecret -No description provided
envSecretsSecret -

Extra environment for the Ansible container, as a secret in dotenv format (NAME=value per line), for playbooks using lookup(‘env’, …).

ansibleParametersString -No description provided
ansibleInventoryTypeString "default"No description provided
ansibleWaitTimeoutInteger 30No description provided
requirementsTemplateString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements.yaml.tmpl"No description provided
requirementsDataString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements-data.yaml"No description provided
cacheBusterString ""

Any value that changes between runs – a timestamp, a CI run id. Threaded down to CreateAnsibleRequirementFiles, where it forces a fresh fetch of the remote requirements instead of a cached render. Leave empty to keep the previous behaviour.

Worth passing from CI: the dagger-labda runner keeps its engine between runs, so without it a merged collection bump can stay invisible to the pipeline indefinitely.

terraformMaxRetriesInteger 3No description provided
terraformRetryDelayInteger 10No description provided
inventoryTypeString "simple"

Inventory type: “simple” (default [all] group) or “cluster” (master/worker groups)

exportPathsString -

Comma-separated list of file paths to export from the Ansible container

agePublicKeySecret -

AGE public key for SOPS encryption of exported files

sopsFileExtensionString "yaml"

File extension for SOPS encryption (e.g., “yaml”, “json”)

sopsConfigFile -

SOPS config file (.sops.yaml)

exportTargetNamesString -

Comma-separated list of target filenames for exported files (maps 1:1 to exportPaths) If not set, original filenames are used

exportDestinationPathString "encrypted-exports"

Destination path for encrypted exports within the result directory Use “./” to place files at the root level (no subdirectory)

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 bake-local --terraform-dir DIR_PATH
func (m *MyModule) Example(terraformDir *dagger.Directory) *dagger.Directory  {
	return dag.
			Vm().
			Bakelocal(terraformDir)
}
@function
def example(terraformdir: dagger.Directory) -> dagger.Directory:
	return (
		dag.vm()
		.bakelocal(terraformdir)
	)
@func()
example(terraformDir: Directory): Directory {
	return dag
		.vm()
		.bakeLocal(terraformDir)
}

bakeLocalByProfile() 🔗

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
srcDirectory !-No description provided
profileFile -No description provided
sopsKeySecret -No description provided
awsAccessKeyIdSecret -No description provided
awsSecretAccessKeySecret -No description provided
vaultRoleIdSecret -No description provided
vaultSecretIdSecret -No description provided
vaultTokenSecret -

vaultToken

vaultUrlSecret -No description provided
ansibleUserSecret -No description provided
ansiblePasswordSecret -No description provided
envSecretsSecret -

Extra environment for the Ansible container, as a secret in dotenv format (NAME=value per line), for playbooks using lookup(‘env’, …).

requirementsTemplateString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements.yaml.tmpl"No description provided
requirementsDataString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements-data.yaml"No description provided
cacheBusterString ""

Any value that changes between runs – a timestamp, a CI run id. Threaded down to CreateAnsibleRequirementFiles, where it forces a fresh fetch of the remote requirements instead of a cached render.

Deliberately NOT a field in execution.yaml: it is a property of THIS run, not of the VM being built, and committing one would make it stale by definition. pr-vm-deploy.yaml should pass the run id.

inventoryTypeString "simple"

Inventory type: “simple” (default [all] group) or “cluster” (master/worker groups)

agePublicKeySecret -

AGE public key for SOPS encryption of exported files

sopsConfigFile -

SOPS config file (.sops.yaml)

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 bake-local-by-profile --src DIR_PATH
func (m *MyModule) Example(src *dagger.Directory) *dagger.Directory  {
	return dag.
			Vm().
			Bakelocalbyprofile(src)
}
@function
def example(src: dagger.Directory) -> dagger.Directory:
	return (
		dag.vm()
		.bakelocalbyprofile(src)
	)
@func()
example(src: Directory): Directory {
	return dag
		.vm()
		.bakeLocalByProfile(src)
}

commitToGit() 🔗

CommitToGit commits a directory of files to a GitHub repository branch. Optionally creates a new branch and opens a pull request.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
sourceDirDirectory !-

Directory containing files to commit

repositoryString !-

Repository in “owner/repo” format

branchNameString "main"

Branch name for git operations

commitMessageString "Add files via Dagger"

Commit message

destinationPathString "/"

Destination path within the repository

gitTokenSecret !-

GitHub token for authentication

createBranchString -

If non-empty, create this branch (from branchName as base) and commit there instead

createPrBoolean -

If true (and createBranch set), open a PR from the new branch back to branchName

prTitleString -

PR title (defaults to commitMessage if empty)

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 commit-to-git --source-dir DIR_PATH --repository string --git-token env:MYSECRET
func (m *MyModule) Example(ctx context.Context, sourceDir *dagger.Directory, repository string, gitToken *dagger.Secret) string  {
	return dag.
			Vm().
			Committogit(ctx, sourceDir, repository, gitToken)
}
@function
async def example(sourcedir: dagger.Directory, repository: str, gittoken: dagger.Secret) -> str:
	return await (
		dag.vm()
		.committogit(sourcedir, repository, gittoken)
	)
@func()
async example(sourceDir: Directory, repository: string, gitToken: Secret): Promise<string> {
	return dag
		.vm()
		.commitToGit(sourceDir, repository, gitToken)
}

executeAnsible() 🔗

Return Type
Boolean !
Arguments
NameTypeDefault ValueDescription
srcDirectory -No description provided
playbooksString !-No description provided
requirementsFile -No description provided
inventoryFile -No description provided
hostsString -

Comma-separated list of hosts (e.g., “192.168.1.10,192.168.1.11”) Used to generate inventory if inventory file is not provided

parametersString -No description provided
parametersFileFile -

Path to a YAML file containing parameters (lower priority)

vaultAppRoleIdSecret -No description provided
vaultSecretIdSecret -No description provided
vaultUrlSecret -No description provided
sshUserSecret -No description provided
sshPasswordSecret -No description provided
envSecretsSecret -

Extra environment for the Ansible container, as a secret in dotenv format (NAME=value per line). Needed by playbooks that resolve values with lookup(‘env’, …), which is evaluated on the controller, not the target – e.g. sthings.container.kind_machinery reads SOPS_AGE_KEY that way.

requirementsTemplateString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements.yaml.tmpl"No description provided
requirementsDataString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements-data.yaml"No description provided
inventoryTypeString "simple"

Inventory type: “simple” (default [all] group) or “cluster” (master/worker groups)

cacheBusterString ""

Any value that changes between runs – a timestamp, a CI run id. Threaded into CreateAnsibleRequirementFiles, where it forces a fresh fetch of the remote requirements instead of a cached render. Leave empty to keep the previous behaviour.

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 execute-ansible --playbooks string
func (m *MyModule) Example(ctx context.Context, playbooks string) bool  {
	return dag.
			Vm().
			Executeansible(ctxplaybooks)
}
@function
async def example(playbooks: str) -> bool:
	return await (
		dag.vm()
		.executeansible(playbooks)
	)
@func()
async example(playbooks: string): Promise<boolean> {
	return dag
		.vm()
		.executeAnsible(playbooks)
}

executeAnsibleEncryptAndCommit() 🔗

ExecuteAnsibleEncryptAndCommit runs Ansible playbooks, extracts files from the container, encrypts them with SOPS, and commits the encrypted files to a Git repository.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
srcDirectory -No description provided
playbooksString !-No description provided
exportPathsString !-

Comma-separated list of file paths to export from the Ansible container

requirementsFile -No description provided
inventoryFile -No description provided
hostsString -

Comma-separated list of hosts (e.g., “192.168.1.10,192.168.1.11”)

parametersString -No description provided
parametersFileFile -No description provided
vaultAppRoleIdSecret -No description provided
vaultSecretIdSecret -No description provided
vaultUrlSecret -No description provided
sshUserSecret -No description provided
sshPasswordSecret -No description provided
envSecretsSecret -

Extra environment for the Ansible container, as a secret in dotenv format (NAME=value per line). Needed by playbooks that resolve values with lookup(‘env’, …), which is evaluated on the controller, not the target.

requirementsTemplateString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements.yaml.tmpl"No description provided
requirementsDataString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements-data.yaml"No description provided
inventoryTypeString "simple"

Inventory type: “simple” (default [all] group) or “cluster” (master/worker groups)

cacheBusterString ""

Any value that changes between runs – a timestamp, a CI run id. Threaded into CreateAnsibleRequirementFiles, where it forces a fresh fetch of the remote requirements instead of a cached render. Leave empty to keep the previous behaviour.

agePublicKeySecret !-

AGE public key for SOPS encryption

sopsFileExtensionString "yaml"

File extension for SOPS encryption (e.g., “yaml”, “json”)

sopsConfigFile -

SOPS config file (.sops.yaml)

gitRepositoryString !-

Git repository in “owner/repo” format

gitBranchString "main"

Git branch name

gitCommitMessageString "Add encrypted files from Ansible execution"

Git commit message

gitDestinationPathString "/"

Destination path within the git repository

gitTokenSecret !-

GitHub token for authentication

gitCreateBranchString -

If non-empty, create this branch (from gitBranch as base) and commit there instead

gitCreatePrBoolean -

If true (and gitCreateBranch set), open a PR from the new branch back to gitBranch

gitPrTitleString -

PR title (defaults to gitCommitMessage if empty)

exportTargetNamesString -

Comma-separated list of target filenames for exported files (maps 1:1 to exportPaths) If not set, original filenames are used

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 execute-ansible-encrypt-and-commit --playbooks string --export-paths string --age-public-key env:MYSECRET --git-repository string --git-token env:MYSECRET
func (m *MyModule) Example(ctx context.Context, playbooks string, exportPaths string, agePublicKey *dagger.Secret, gitRepository string, gitToken *dagger.Secret) string  {
	return dag.
			Vm().
			Executeansibleencryptandcommit(ctxplaybooks, exportPaths, agePublicKey, gitRepository, gitToken)
}
@function
async def example(playbooks: str, exportpaths: str, agepublickey: dagger.Secret, gitrepository: str, gittoken: dagger.Secret) -> str:
	return await (
		dag.vm()
		.executeansibleencryptandcommit(playbooks, exportpaths, agepublickey, gitrepository, gittoken)
	)
@func()
async example(playbooks: string, exportPaths: string, agePublicKey: Secret, gitRepository: string, gitToken: Secret): Promise<string> {
	return dag
		.vm()
		.executeAnsibleEncryptAndCommit(playbooks, exportPaths, agePublicKey, gitRepository, gitToken)
}

executeAnsibleWithExport() 🔗

ExecuteAnsibleWithExport runs Ansible playbooks and exports specified files from the container. Same parameters as ExecuteAnsible plus exportPaths (comma-separated file paths to extract).

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
srcDirectory -No description provided
playbooksString !-No description provided
exportPathsString !-

Comma-separated list of file paths to export from the Ansible container

requirementsFile -No description provided
inventoryFile -No description provided
hostsString -

Comma-separated list of hosts (e.g., “192.168.1.10,192.168.1.11”) Used to generate inventory if inventory file is not provided

parametersString -No description provided
parametersFileFile -

Path to a YAML file containing parameters (lower priority)

vaultAppRoleIdSecret -No description provided
vaultSecretIdSecret -No description provided
vaultUrlSecret -No description provided
sshUserSecret -No description provided
sshPasswordSecret -No description provided
envSecretsSecret -

Extra environment for the Ansible container, as a secret in dotenv format (NAME=value per line). Needed by playbooks that resolve values with lookup(‘env’, …), which is evaluated on the controller, not the target.

requirementsTemplateString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements.yaml.tmpl"No description provided
requirementsDataString "https://raw.githubusercontent.com/stuttgart-things/ansible/refs/heads/main/templates/requirements-data.yaml"No description provided
inventoryTypeString "simple"

Inventory type: “simple” (default [all] group) or “cluster” (master/worker groups)

cacheBusterString ""

Any value that changes between runs – a timestamp, a CI run id. Threaded into CreateAnsibleRequirementFiles, where it forces a fresh fetch of the remote requirements instead of a cached render. Leave empty to keep the previous behaviour.

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 execute-ansible-with-export --playbooks string --export-paths string
func (m *MyModule) Example(playbooks string, exportPaths string) *dagger.Directory  {
	return dag.
			Vm().
			Executeansiblewithexport(playbooks, exportPaths)
}
@function
def example(playbooks: str, exportpaths: str) -> dagger.Directory:
	return (
		dag.vm()
		.executeansiblewithexport(playbooks, exportpaths)
	)
@func()
example(playbooks: string, exportPaths: string): Directory {
	return dag
		.vm()
		.executeAnsibleWithExport(playbooks, exportPaths)
}

executeTerraform() 🔗

ExecuteTerraform runs terraform with optional SOPS-encrypted file decryption, optional Kubernetes-secret retrieval (e.g. VAULT_TOKEN injected as a tfvar), optional kubeconfig backend support, and AWS/Vault credentials. Returns the terraform working directory after execution. This is the canonical Terraform execution entry point — the configuration module previously hosted a duplicate (TerraformApply) which has been removed.

Return Type
Directory !
Arguments
NameTypeDefault ValueDescription
terraformDirDirectory !-

Directory containing terraform configurations

operationString "apply"

Terraform operation to execute

variablesString -

Comma-separated terraform variables (e.g. “name=patrick,food=schnitzel”)

awsAccessKeyIdSecret -

AWS access key ID for S3/MinIO backend

awsSecretAccessKeySecret -

AWS secret access key for S3/MinIO backend

vaultRoleIdSecret -

Vault role ID secret

vaultSecretIdSecret -

Vault secret ID secret

vaultTokenSecret -

Vault token secret

sopsAgeKeySecret -

AGE key for SOPS decryption of encryptedFiles / encryptedKubeConfig

encryptedFilesString -

Comma-separated list of SOPS-encrypted file paths under terraformDir to decrypt (e.g. “terraform.tfvars.sops.json,secrets.sops.yaml”)

kubeConfigSecret -

Kubeconfig secret for Kubernetes state backend access (plaintext)

kubeConfigPathString "/root/.kube/config"

Path to mount the kubeconfig inside the container (must match backend config_path in backend.tf)

encryptedKubeConfigFile -

SOPS-encrypted kubeconfig file; decrypted with sopsAgeKey and used for kubectl

kubeSecretNameString -

Kubernetes secret name to read (e.g. “vault-root-token”)

kubeSecretNamespaceString -

Kubernetes namespace for the secret

kubeSecretJsonpathString -

JSONPath expression to extract from the Kubernetes secret (e.g. “.data.root_token”)

kubeSecretTfVarString -

Terraform variable name to set from the Kubernetes secret value (e.g. “vault_token” becomes -var vault_token=)

exportTfOutputBoolean -

Run terraform output –json after apply and write result to output.json

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 execute-terraform --terraform-dir DIR_PATH
func (m *MyModule) Example(terraformDir *dagger.Directory) *dagger.Directory  {
	return dag.
			Vm().
			Executeterraform(terraformDir)
}
@function
def example(terraformdir: dagger.Directory) -> dagger.Directory:
	return (
		dag.vm()
		.executeterraform(terraformdir)
	)
@func()
example(terraformDir: Directory): Directory {
	return dag
		.vm()
		.executeTerraform(terraformDir)
}

outputTerraformRun() 🔗

OutputTerraformRun runs terraform output --json against an already-applied terraform directory. Supports AWS S3/MinIO and Kubernetes state backends.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
terraformDirDirectory !-

Directory containing terraform state (output of ExecuteTerraform)

awsAccessKeyIdSecret -

AWS access key ID for S3/MinIO backend

awsSecretAccessKeySecret -

AWS secret access key for S3/MinIO backend

kubeConfigSecret -

Kubeconfig secret for Kubernetes backend access

kubeConfigPathString "/root/.kube/config"

Path to mount the kubeconfig inside the container (must match backend config_path in backend.tf)

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 output-terraform-run --terraform-dir DIR_PATH
func (m *MyModule) Example(ctx context.Context, terraformDir *dagger.Directory) string  {
	return dag.
			Vm().
			Outputterraformrun(ctx, terraformDir)
}
@function
async def example(terraformdir: dagger.Directory) -> str:
	return await (
		dag.vm()
		.outputterraformrun(terraformdir)
	)
@func()
async example(terraformDir: Directory): Promise<string> {
	return dag
		.vm()
		.outputTerraformRun(terraformDir)
}

outputTerraformRunWithCreds() 🔗

OutputTerraformRunWithCreds is a back-compat alias for OutputTerraformRun limited to the AWS credentials path.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
terraformDirDirectory !-No description provided
awsAccessKeyIdSecret -No description provided
awsSecretAccessKeySecret -No description provided
Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 output-terraform-run-with-creds --terraform-dir DIR_PATH
func (m *MyModule) Example(ctx context.Context, terraformDir *dagger.Directory) string  {
	return dag.
			Vm().
			Outputterraformrunwithcreds(ctx, terraformDir)
}
@function
async def example(terraformdir: dagger.Directory) -> str:
	return await (
		dag.vm()
		.outputterraformrunwithcreds(terraformdir)
	)
@func()
async example(terraformDir: Directory): Promise<string> {
	return dag
		.vm()
		.outputTerraformRunWithCreds(terraformDir)
}

renderHarvesterVm() 🔗

RenderHarvesterVm renders the PVC, cloud-init Secret and VirtualMachine from the harvester-vm KCL module into a single apply-ready multi-document YAML.

Exported on its own so a run can be inspected before anything touches a cluster (... render-harvester-vm ... | tee vm.yaml), and so the render can be reused for GitOps-style flows.

The KCL module’s top-level value is an items: list; dagger/kcl’s Run post-processor converts exactly that shape into multi-document YAML when formatOutput is on (its default), so the yq/awk splitting the module’s README shows for the bare kcl run case is not needed here.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
ociSourceString "ghcr.io/stuttgart-things/harvester-vm:0.2.0"No description provided
kclParametersFileFile -No description provided
kclParametersString -No description provided
encryptedFileFile -No description provided
sopsKeySecret -No description provided
Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 render-harvester-vm
func (m *MyModule) Example() *dagger.File  {
	return dag.
			Vm().
			Renderharvestervm()
}
@function
def example() -> dagger.File:
	return (
		dag.vm()
		.renderharvestervm()
	)
@func()
example(): File {
	return dag
		.vm()
		.renderHarvesterVm()
}

waitForVmIp() 🔗

WaitForVmIp polls a KubeVirt VirtualMachineInstance until it is Running and the in-guest QEMU guest agent has reported an IP address, and returns that address.

Two things this deliberately does NOT do:

It does not use kubectl wait. The kubernetes module wraps every Command in (... 2>&1) || true, so a wait that times out would come back as a success carrying an error message — the run would sail on and point Ansible at nothing. Polling and enforcing the deadline here keeps the failure a failure.

It does not issue the same kubectl call twice. Dagger caches an exec by the digest of its arguments and inputs, so an unchanged kubectl get would be served from cache and the loop would spin forever on the first (empty) answer. The per-attempt marker in additionalCommand is what keeps every poll a real call — it is a shell comment, so it changes the digest and nothing else.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
kubeConfigSecret !-

Kubeconfig for the cluster running the VM.

vmNameString !-

VM name (the VMI carries the same name as its VirtualMachine).

namespaceString "default"No description provided
waitTimeoutInteger 900No description provided
waitIntervalInteger 15No description provided
vmiAppearTimeoutInteger 120

Seconds to wait for the VMI object to exist at all before giving up. The VMI appears seconds after the VirtualMachine is applied; it is the IP that takes minutes. Bounding the two separately turns “the VM was never created” into a fast failure instead of a full –wait-timeout. 0 disables it, folding the check back into –wait-timeout.

Example
dagger -m github.com/stuttgart-things/blueprints/vm@1b60e838b79287aea859cb9c612865ae0812b5f1 call \
 wait-for-vm-ip --kube-config env:MYSECRET --vm-name string
func (m *MyModule) Example(ctx context.Context, kubeConfig *dagger.Secret, vmName string) string  {
	return dag.
			Vm().
			Waitforvmip(ctx, kubeConfig, vmName)
}
@function
async def example(kubeconfig: dagger.Secret, vmname: str) -> str:
	return await (
		dag.vm()
		.waitforvmip(kubeconfig, vmname)
	)
@func()
async example(kubeConfig: Secret, vmName: string): Promise<string> {
	return dag
		.vm()
		.waitForVmIp(kubeConfig, vmName)
}