-
Unlock Vault:
Invoke smaqit.infrastructure-vault-loader and confirm Vault is running, unsealed, and all
secret/<project-slug>/* paths are populated. Do not proceed until this is confirmed.
-
Fetch credentials from Vault into shell environment:
export VAULT_ADDR=http://127.0.0.1:8200
export PROJECT_SLUG=<project-slug>
export TF_VAR_app_credential_id=$(vault kv get -field=app_credential_id secret/${PROJECT_SLUG}/cyso)
export TF_VAR_app_credential_secret=$(vault kv get -field=app_credential_secret secret/${PROJECT_SLUG}/cyso)
export TF_VAR_github_token=$(vault kv get -field=token secret/${PROJECT_SLUG}/github)
export TF_VAR_ssh_public_key=$(vault kv get -field=public_key secret/${PROJECT_SLUG}/ssh)
export AWS_ACCESS_KEY_ID=$(vault kv get -field=access_key secret/${PROJECT_SLUG}/tfstate)
export AWS_SECRET_ACCESS_KEY=$(vault kv get -field=secret_key secret/${PROJECT_SLUG}/tfstate)
Use TF_VAR_github_token — not GITHUB_TOKEN (reserved by Actions) and not TF_VAR_GITHUB_TOKEN
(case-sensitive; Terraform maps TF_VAR_github_token → var.github_token). The public key is
already newline-stripped at Vault load time — do not tr -d '\n' again.
-
Confirm Ubuntu image ID and flavor (catalog values change occasionally):
openstack image list | grep "Ubuntu 22.04"
openstack flavor list
If IDs differ from the defaults in deployment/terraform/main.tf, update that file before continuing.
Note: openstack CLI must be available; auth is implicit via TF_VAR_app_credential_* env vars
set above — no OpenRC file is required.
-
Navigate to Terraform directory:
cd deployment/terraform
-
Initialize Terraform:
terraform init
Confirms remote state backend connectivity. .terraform.lock.hcl is committed; if regeneration is
needed: terraform providers lock -platform=linux_amd64 -platform=darwin_arm64.
-
Review plan. Expected resources on first apply: 1 Nova VM, 1 boot volume (20 GB),
1 security group (ports 22/80/443), 1 keypair, 1 GitHub Actions variable.
terraform plan
-
Apply:
terraform apply
After apply, note the fixed_ip output — this is the public address. The floating IP is provisioned
but does not route on Cyso's flat network; ignore it.
-
Verify SSH access (should succeed within 60 seconds of apply):
TMPKEY=$(mktemp) && trap "rm -f $TMPKEY" EXIT
vault kv get -field=private_key secret/${PROJECT_SLUG}/ssh > "$TMPKEY"
chmod 600 "$TMPKEY"
ssh -i "$TMPKEY" ubuntu@<fixed_ip>
-
Floating IP does not route — Cyso assigns publicly-routable IPs directly to VM interfaces on the
flat network. openstack_networking_floatingip_associate_v2 does not make it publicly accessible.
Always use outputs.fixed_ip; never the floating IP value.
-
Data volume is /dev/sdb, not /dev/vdb — Cyso presents Cinder volumes as SCSI devices, not
virtio-blk. lsblk shows it as sdb (or sdc if multiple volumes are attached).
-
Keypair trailing-newline drift — Terraform will show the keypair needing replacement on a
non-first apply if the stored key string differs by a trailing newline. Strip the newline when
loading into Vault:
vault kv put secret/<slug>/ssh public_key="$(cat ~/.ssh/<key>.pub | tr -d '\n')" ...
The Vault fetch (vault kv get -field=public_key) does not add a newline, so subsequent
applies are stable. The variable name is TF_VAR_ssh_public_key (all lowercase) — matches
var.ssh_public_key in variables.tf.
-
Provider lock file platform mismatch — .terraform.lock.hcl is committed with hashes for
linux_amd64 and darwin_arm64. Regenerate if running on a different platform:
terraform providers lock -platform=<platform>.
-
Application credential must be project-scoped — a user-level credential without project scope
will fail on resource creation.
-
Do not run terraform destroy — it tears down live infrastructure. Not part of any deployment
or acceptance step.