Skip to content

Resource

The resource is the fundamental building block of HCL configuration. Resources represent infrastructure components, services, and other entities that make up your lab environment. Each resource has a specific type (e.g., container, network, template) and defines the desired state for that component.

As a lab author, you use resources to build complete lab environments:

  • Infrastructure Components: Define containers, networks, and storage volumes that form your lab’s foundation
  • Service Configuration: Configure databases, web servers, APIs, and other services needed for hands-on exercises
  • User Interface Elements: Create terminals, editors, services, and external websites that users interact with
  • Content and Tasks: Define pages, tasks, quizzes, and other instructional content that guide users through the lab
  • Utility Operations: Use exec, template, and file resources to configure systems and prepare lab environments
  • Cloud Integrations: Provision cloud resources like AWS accounts, Azure subscriptions, or Google Cloud projects

Resources work together to create comprehensive, interactive learning environments where users can practice real-world skills.

resource "resource_type" "name" {
# Configuration fields specific to resource type
}
resource "container" "web_server" {
# Resource-specific configuration
image {
name = "nginx:latest"
}
container_name = "web-server"
port {
local = 8080
remote = 80
}
# Common resource fields (available on all resources)
depends_on = ["resource.network.main"]
disabled = false
}

All resources inherit these fields from the base resource type:

FieldRequiredTypeDescription
depends_onlist(string)List of resources this resource depends on (delays creation until dependencies are ready)
disabledboolWhether this resource is disabled and should be skipped during processing. Defaults to false.

Every resource automatically gets metadata fields that can be referenced:

Field Type Description
meta.id string Unique identifier for the resource
meta.name string Resource name (the label you specified)
meta.type string Resource type (e.g., "container", "network")
meta.status string Current status of the resource (e.g., "running", "stopped")

Resources are organized into categories based on their functionality:

Resource Type Purpose Reference
container Docker containers for applications and services Container Reference
network Isolated networks for container communication Network Reference
ingress Network ingress rules and load balancing Ingress Reference
volume Persistent storage volumes Volume Reference
Resource Type Purpose Reference
kubernetes_cluster Kubernetes clusters for container orchestration Kubernetes Cluster Reference
nomad_cluster Nomad clusters for workload orchestration Nomad Cluster Reference
Resource Type Purpose Reference
terminal Interactive terminal sessions Terminal Reference
editor Code editors with syntax highlighting Editor Reference
service Web services and applications Service Reference
external_website External web resources and documentation External Website Reference
Resource Type Purpose Reference
lab Lab configuration and metadata Lab Reference
page Instruction pages with markdown content Page Reference
task Interactive tasks with validation Task Reference
note Contextual notes and hints Note Reference
quiz Knowledge assessment quizzes Quiz Reference
Resource Type Purpose Reference
exec Execute commands and scripts Exec Reference
template Generate files from templates Template Reference
http HTTP requests and API interactions HTTP Reference
terraform Terraform infrastructure provisioning Terraform Reference

Resources are referenced using the format:

resource.resource_type.resource_name
# Reference a container's name
resource.container.web.meta.name
# Reference a network's ID
resource.network.backend.meta.id
# Reference a service's URL
resource.service.api.url
# Reference computed attributes
resource.kubernetes_cluster.main.kubeconfig_path
resource "container" "database" {
image {
name = "postgres:13"
}
container_name = "postgres-db"
environment = {
POSTGRES_DB = "myapp"
POSTGRES_USER = "admin"
POSTGRES_PASSWORD = "password"
}
port {
local = 5432
remote = 5432
}
}
resource "network" "backend" {
subnet = "10.0.1.0/24"
}
resource "container" "app" {
depends_on = ["resource.network.backend"]
image {
name = "myapp:latest"
}
network {
id = resource.network.backend.meta.id
}
}
resource "container" "web" {
image {
name = "nginx:latest"
}
container_name = "web-server"
port {
local = 8080
remote = 80
}
}
resource "service" "web_ui" {
target = resource.container.web
port = 8080
title = "Web Application"
}
resource "container" "debug_tools" {
disabled = !variable.debug_enabled
image {
name = "busybox:latest"
}
container_name = "debug-container"
}
resource "kubernetes_cluster" "main" {
network {
id = resource.network.k8s.meta.id
}
node "server" {
cpu = 2048
memory = 4096
node_role = "control-plane"
}
node "worker" {
cpu = 1024
memory = 2048
node_role = "worker"
}
config {
api_server_port = 6443
service_cidr = "10.96.0.0/12"
pod_cidr = "10.244.0.0/16"
}
}
# 1. Create network
resource "network" "lab_network" {
subnet = "10.0.0.0/16"
}
# 2. Create database container
resource "container" "database" {
depends_on = ["resource.network.lab_network"]
image {
name = "postgres:13"
}
network {
id = resource.network.lab_network.meta.id
}
environment = {
POSTGRES_DB = "appdb"
}
}
# 3. Create application container
resource "container" "app" {
depends_on = ["resource.container.database"]
image {
name = "myapp:latest"
}
network {
id = resource.network.lab_network.meta.id
}
environment = {
DATABASE_HOST = resource.container.database.meta.name
}
}
# 4. Create user interface
resource "service" "app_ui" {
target = resource.container.app
port = 8080
title = "My Application"
}
# 5. Create terminal for access
resource "terminal" "app_terminal" {
target = resource.container.app
shell = "/bin/bash"
}
  1. Meaningful Names: Use descriptive resource names that indicate their purpose
  2. Explicit Dependencies: Use depends_on when resources need specific creation ordering
  3. Resource Organization: Group related resources logically in your configuration
  4. Reference Patterns: Use resource references instead of hardcoding values
  5. Conditional Resources: Use the disabled field for optional components
  6. Documentation: Include comments explaining complex resource configurations
# Database tier
resource "container" "database" {
image {
name = "postgres:13"
}
container_name = "db-server"
}
# Application tier
resource "container" "api" {
depends_on = ["resource.container.database"]
image {
name = "api:latest"
}
environment = {
DB_HOST = resource.container.database.meta.name
}
}
# Presentation tier
resource "container" "web" {
depends_on = ["resource.container.api"]
image {
name = "web:latest"
}
environment = {
API_URL = format("http://%s:8080", resource.container.api.meta.name)
}
}
resource "container" "dev_environment" {
image {
name = "ubuntu:20.04"
}
volume {
source = "./workspace"
destination = "/workspace"
type = "bind"
}
}
resource "editor" "code_editor" {
target = resource.container.dev_environment
path = "/workspace"
}
resource "terminal" "dev_terminal" {
target = resource.container.dev_environment
working_directory = "/workspace"
}
resource "network" "services" {
subnet = "10.0.10.0/24"
}
# Create multiple services
resource "container" "services" {
count = length(variable.service_names)
image {
name = "${variable.service_names[count.index]}:latest"
}
container_name = variable.service_names[count.index]
network {
id = resource.network.services.meta.id
}
}
# Service discovery configuration
output "service_endpoints" {
value = {
for i, name in variable.service_names : name => {
host = resource.container.services[i].meta.name
port = 8080
}
}
}

Resources follow a predictable lifecycle:

  1. Parsing: HCL configuration is parsed and validated
  2. Dependency Resolution: Resource dependencies are analyzed and ordering determined
  3. Creation: Resources are created in dependency order
  4. Ready State: Resources become available for reference by other resources
  5. Runtime: Resources remain active during lab execution
  6. Cleanup: Resources are cleaned up when the lab ends

Understanding this lifecycle helps you design configurations that work reliably and predictably across different lab environments.