feat: mostly terraform shenanigans, but TF can now create ssh keys and servers

This commit is contained in:
allanice001
2025-11-02 17:18:28 +00:00
parent 0d10d42442
commit 43f8549320
59 changed files with 6353 additions and 28 deletions

View File

@@ -5,7 +5,7 @@ provider "glueops/autoglue/autoglue" {
version = "0.0.1"
constraints = "0.0.1"
hashes = [
"h1:XW1zYWB6NTuE7jgJwWAkZeBBhL3Me36KE4Puy6lN6+o=",
"h1:K5xMCf5zxZVCurwzkSEAaMv70dzBlVU8VN/q72sNyD0=",
]
}

View File

@@ -1 +1 @@
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"ssh","Source":"../../modules/ssh-key","Dir":"../../modules/ssh-key"}]}
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"servers","Source":"../../modules/servers","Dir":"../../modules/servers"},{"Key":"ssh","Source":"../../modules/ssh-key","Dir":"../../modules/ssh-key"}]}

View File

@@ -27,3 +27,47 @@ output "ssh_public_keys" {
output "ssh_written_files" {
value = { for k, m in module.ssh : k => m.written_files }
}
module "servers" {
source = "../../modules/servers"
# Wire the SSH key IDs so servers can reference them by name
ssh_key_ids = { for k, m in module.ssh : k => m.id }
servers = {
bastion = {
hostname = "bastion-01"
private_ip_address = "10.0.0.10"
public_ip_address = "54.12.34.56" # required for role=bastion
role = "bastion"
ssh_user = "ubuntu"
ssh_key_ref = "bastionKey" # points to module.ssh["bastionKey"].id
status = "pending"
}
manager1 = {
hostname = "k3s-mgr-01"
private_ip_address = "10.0.1.11"
role = "manager"
ssh_user = "ubuntu"
ssh_key_ref = "clusterKey"
status = "pending"
}
agent1 = {
hostname = "k3s-agent-01"
private_ip_address = "10.0.2.21"
role = "agent"
ssh_user = "ubuntu"
ssh_key_ref = "clusterKey"
status = "pending"
}
}
}
output "server_ids" {
value = module.servers.ids
}
output "server_statuses" {
value = module.servers.statuses
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,22 +2,22 @@ org_key = "org_lnJwmyyWH7JC-JgZo5v3Kw"
org_secret = "fqd9yebGMfK6h5HSgWn4sXrwr9xlFbvbIYtNylRElMQ"
ssh_keys = {
key1 = {
name = "CI deploy key 1"
comment = "deploy1@autoglue"
bastionKey = {
name = "Bastion Key"
comment = "deploy@autoglue"
type = "rsa"
bits = 4096
enable_download = true
download_part = "both"
download_dir = "out/key1"
download_dir = "out/bastionKey"
}
key2 = {
name = "CI deploy key 2"
comment = "deploy2@autoglue"
clusterKey = {
name = "Cluster Key"
comment = "bastion@autoglue"
type = "ed25519" # bits ignored
enable_download = true
download_part = "both"
download_dir = "out/key2"
download_dir = "out/clusterKey"
}
}

View File

@@ -0,0 +1,39 @@
locals {
# Resolve the SSH key ID for each server:
# Prefer explicit ssh_key_id, otherwise look up by ssh_key_ref in var.ssh_key_ids.
resolved_ssh_key_ids = {
for name, spec in var.servers :
name => coalesce(
try(spec.ssh_key_id, null),
try(var.ssh_key_ids[spec.ssh_key_ref], null)
)
}
}
resource "autoglue_server" "this" {
for_each = var.servers
hostname = try(each.value.hostname, null)
private_ip_address = each.value.private_ip_address
public_ip_address = try(each.value.public_ip_address, null)
role = lower(each.value.role)
ssh_user = each.value.ssh_user
ssh_key_id = local.resolved_ssh_key_ids[each.key]
status = try(each.value.status, null)
# Client-side guards to match your API rules
lifecycle {
precondition {
condition = local.resolved_ssh_key_ids[each.key] != null && local.resolved_ssh_key_ids[each.key] != ""
error_message = "Provide either ssh_key_id or ssh_key_ref (and pass ssh_key_ids to the module)."
}
precondition {
condition = lower(each.value.role) != "bastion" ? true : (try(each.value.public_ip_address, "") != "")
error_message = "public_ip_address is required when role == \"bastion\"."
}
precondition {
condition = try(each.value.status, "") == "" || contains(["pending", "provisioning", "ready", "failed"], lower(each.value.status))
error_message = "status must be one of: pending, provisioning, ready, failed (or omitted)."
}
}
}

View File

@@ -0,0 +1,28 @@
output "ids" {
description = "Map of server IDs by key."
value = { for k, r in autoglue_server.this : k => r.id }
}
output "statuses" {
description = "Map of server statuses by key."
value = { for k, r in autoglue_server.this : k => r.status }
}
output "details" {
description = "Selected attributes for convenience."
value = {
for k, r in autoglue_server.this : k => {
id = r.id
organization_id = r.organization_id
hostname = r.hostname
private_ip_address = r.private_ip_address
public_ip_address = r.public_ip_address
role = r.role
ssh_user = r.ssh_user
ssh_key_id = r.ssh_key_id
status = r.status
created_at = r.created_at
updated_at = r.updated_at
}
}
}

View File

@@ -0,0 +1,34 @@
variable "servers" {
description = <<-EOT
Map of servers to create. Example shape:
{
bastion = {
hostname = "bastion-01"
private_ip_address = "10.0.0.10"
public_ip_address = "54.12.34.56" # required when role = "bastion"
role = "bastion"
ssh_user = "ubuntu"
ssh_key_ref = "bastionKey" # OR set ssh_key_id instead
# ssh_key_id = "uuid-string"
# status = "pending|provisioning|ready|failed"
}
}
EOT
type = map(object({
hostname = optional(string)
private_ip_address = string
public_ip_address = optional(string)
role = string
ssh_user = string
ssh_key_ref = optional(string) # name to look up in var.ssh_key_ids
ssh_key_id = optional(string) # direct UUID (overrides ssh_key_ref if set)
status = optional(string) # pending|provisioning|ready|failed
}))
default = {}
}
variable "ssh_key_ids" {
description = "Map of SSH key IDs you can reference via servers[*].ssh_key_ref."
type = map(string)
default = {}
}

View File

@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.5.0"
required_providers {
autoglue = {
source = "glueops/autoglue/autoglue"
}
}
}