Spacelift: Difference between revisions
Document how the lab was built |
Use JS-injected video placeholder (raw video tag gets sanitized) |
||
| (3 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
This page documents how the [[Main Page|AWS/IaC practice lab]] actually got built — two working environments, run through Spacelift, with real gotchas along the way. See also: [[MAC]], the agent that did most of the hands-on work. | This page documents how the [[Main Page|AWS/IaC practice lab]] actually got built — two working environments, run through Spacelift, with real gotchas along the way. See also: [[How to Use Spacelift]] for the step-by-step version, and [[MAC]], the agent that did most of the hands-on work. | ||
== Watch it happen == | |||
Brett sketches the architecture, MAC turns it into OpenTofu config, Spacelift plans it and waits for a human to confirm before anything touches AWS. That's the whole loop: | |||
<div class="wiki-video-frame" id="wiki-video-spacelift-build"></div> | |||
== Why it exists == | == Why it exists == | ||
| Line 57: | Line 63: | ||
That last point is the one that actually matters most: it's the reason handing real, live infrastructure work to [[MAC]] was something to trust rather than something to worry about. | That last point is the one that actually matters most: it's the reason handing real, live infrastructure work to [[MAC]] was something to trust rather than something to worry about. | ||
[[Category:AWS | [[Category:Infrastructure as Code]] | ||
[[Category:AWS]] | |||
[[Category:AI Agents]] | |||
Latest revision as of 19:18, 21 August 2026
This page documents how the AWS/IaC practice lab actually got built — two working environments, run through Spacelift, with real gotchas along the way. See also: How to Use Spacelift for the step-by-step version, and MAC, the agent that did most of the hands-on work.
Watch it happen
[edit]Brett sketches the architecture, MAC turns it into OpenTofu config, Spacelift plans it and waits for a human to confirm before anything touches AWS. That's the whole loop:
Why it exists
[edit]Practice, ahead of an interview with Spacelift itself. The goal wasn't a tutorial-grade demo — it was a real AWS account, real infrastructure, real failure modes, so the mistakes happened here instead of live.
The two labs
[edit]Orbit Labs (two-stack)
[edit]Two independent Spacelift stacks, one Git repo each, linked by a Spacelift-level stack dependency rather than a Terraform data source:
- Networking stack — VPC (
10.0.0.0/16), one public subnet, an internet gateway, and a public route table. Outputssubnet_id. - App stack — consumes
subnet_idasvar.subnet_id. Builds a key pair, two security groups (web: 80/443/22 from an admin-only CIDR; db: 3306 from the web SG only), a pinned-AMI web fleet across prod/dev/test environments, and one dedicated database instance.
The app stack cannot plan until the networking stack has applied and produced that output — enforced on Spacelift's Dependencies tab, not just documented convention.
Pulsar (single-instance)
[edit]A simpler, single self-contained stack built later specifically to avoid the stack-dependency wiring above for quick experiments: one VPC, one t3.micro/t3.small instance, no cross-stack linkage. 443 open to the world by design (for testing real internet-facing TLS); 22 restricted to an admin IP. An Elastic IP keeps the address stable across stop/start and resize cycles.
Build order
[edit]- Push the networking/base stack first; wait for
FINISHED. - Push the app stack; it fails outright with a missing-variable error if pushed before the dependency exists.
- Poll with
spacectl, not the browser — the web UI lags behind real state. - Pull outputs (
spacectl stack outputs --id <stack-id>) for live IPs once finished.
What actually broke
[edit]- AMI drift wiped a fleet. A
data "aws_ami" { most_recent = true }lookup meant a routine, unrelated push replaced the entire web fleet the moment AWS published a new image — new IPs, and any state that only lived on local disk was gone. Fix: pin the AMI as a fixed-default variable, bump it deliberately. - The AWS console reads a literal
Nametag. Resources tagged with lowercasenameare valid but invisible in the console's Name column. Every resource needsName = "...", capital N. - Deleting a stack doesn't clean up its dependency link. Emptying the networking stack's config to tear it down left the app stack's dependency reference dangling — the next run on either stack failed with a vague missing-input error until the link was removed and, later, explicitly re-added before rebuilding.
- Free-tier restrictions fail mid-operation. Requesting a
t3.mediumresize got rejected by aFreeTierRestrictionError— but only after AWS had already stopped the instance to attempt the resize, leaving it stopped (and, without an Elastic IP already attached, on a fresh IP). - Uploaded-file PHP execution was silently broken. Because the vhost proxies every
*.phprequest straight to PHP-FPM at the server-config level, MediaWiki's and WordPress's default "disable script execution in the uploads directory".htaccessprotection didn't actually apply — confirmed by dropping a harmless.phptest file inwp-content/uploads/and watching it execute. Fixed with an explicitSetHandler none+Require all deniedoverride in both upload directories.
The pattern that's worth keeping
[edit]Instance power state as a declarative resource, not a manual CLI step:
resource "aws_ec2_instance_state" "web" {
instance_id = aws_instance.web.id
state = "stopped" # flip to "running", push, confirm
}
Flip the string, push, Spacelift applies it. No compute billing while stopped; the Elastic IP means the address survives the whole cycle.
Why Spacelift specifically
[edit]- Plan and apply are structurally separate, with a mandatory human confirm gate before anything touches AWS.
- Stack dependencies are a first-class, enforced object — not a README instruction to run things in order.
- Full run history via
spacectl stack run list— every change, when, and why, queryable later. - The automation API key is scoped read-only by design; it hard-fails on anything that tries to trigger a deploy. Read access for tooling and write access for a human are two separate credentials.
That last point is the one that actually matters most: it's the reason handing real, live infrastructure work to MAC was something to trust rather than something to worry about.