Skip to content

[User Story] Ephemeral Node Enrollment & Cleanup #165

@noahwhite

Description

@noahwhite

Story Summary

As a Cloud Operator, I want Flatcar nodes to automatically enroll in Aembit at boot and retire themselves upon destruction, so that I stay within the 10-client free-tier limit regardless of how many times I recreate the environment.

Phase: 1 - Identity & Provisioning (The "Secret Zero" Solution)


✅ Acceptance Criteria

  • aembit_workload_instance resource tied to Vultr VM lifecycle
  • Enrollment token generated per-instance, tied to instance_replacement_hash
  • Token injected into Ignition userdata for boot-time enrollment
  • terraform destroy successfully removes workload instance from Aembit tenant
  • Re-running terraform apply (instance recreation) works without manual cleanup
  • Aembit console shows correct active client count

📝 Additional Context

Lifecycle Management

terraform apply (new instance)
    │
    ├─► Creates aembit_workload_instance resource
    │   - Generates enrollment token
    │   - Token valid for single use
    │
    ├─► Vultr instance boots
    │   - Reads enrollment token from Ignition
    │   - Enrolls with Aembit (consumes token)
    │   - Instance now authenticated
    │
    └─► Active clients: 1

terraform destroy
    │
    ├─► Destroys Vultr instance
    │
    └─► Destroys aembit_workload_instance
        - Removes client from Aembit tenant
        - Active clients: 0

OpenTofu Resources

resource "aembit_workload_instance" "ghost" {
  name        = "ghost-${var.environment}-${var.domain_slug}"
  description = "Ghost instance on Vultr"
  
  # Tied to instance lifecycle
  lifecycle {
    create_before_destroy = false
  }
}

resource "aembit_enrollment_token" "ghost" {
  workload_instance_id = aembit_workload_instance.ghost.id
  
  # Regenerate when instance is replaced
  keepers = {
    instance_hash = local.instance_replacement_hash
  }
  
  # Short-lived, single use
  expires_in = "1h"
  max_uses   = 1
}

# Inject into Ignition
data "ct_config" "ghost" {
  content = templatefile("ghost.bu", {
    aembit_enrollment_token = aembit_enrollment_token.ghost.token
  })
}

Free Tier Considerations

  • Aembit free tier: 10 active clients
  • Each Ghost instance = 1 client
  • Without cleanup: apply/destroy cycles accumulate stale clients
  • With this story: clients automatically cleaned up on destroy

Dependencies

  • GHO-65: OIDC federation must be configured first

📦 Definition of Ready

  • Acceptance criteria defined
  • Blocked by GHO-65 (OIDC federation)
  • Story is estimated
  • Team has necessary skills and access
  • Priority is clear
  • Business value understood

✅ Definition of Done

  • All acceptance criteria met
  • tofu plan shows workload instance creation
  • tofu destroy removes client from Aembit
  • Multiple apply/destroy cycles verified
  • Free tier limit not exceeded

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions