# 07: Optional Cloud Infrastructure

## Outcome

Opt in to one small GCP virtual machine through a maintained cloud image,
record the exact created-resource evidence, and prove teardown with both a
scheduled limit and an explicit destroy action.

## Prerequisites

- Complete Tutorials 01 through 06.
- Explicit authority to create and delete Compute Engine, VPC, subnetwork, and
  firewall resources in a disposable GCP project.
- A dedicated service account with only the permissions required by the
  reviewed Terraform resources. Do not use an owner credential.
- The approved public egress IPv4 address of the operator, expressed as one
  `/32` source prefix. This lesson rejects broader ingress.
- Permission to manage your User Secret and run the lesson App.
- A current budget alert and quota for one `e2-small` VM. Allow 45 minutes.

This lesson is optional. It can incur GCP compute, address, network, storage,
and egress charges. The default repository gate never runs it.

## Files

- `Dockerfile` and `run.sh` own a confirmation guard image.
- `cookbook.yaml` owns the lesson flow, names-only `GOOGLE_CREDENTIALS`
  declaration, one-hour teardown schedule, and explicit destroy recipe.
- `../../cookbooks/lemc-gcp-terraform-vm/` is the maintained cloud image
  source. This tutorial does not copy or fork that implementation.

## Web

1. Review the cloud image source and Terraform plan with the project owner.
   Confirm its exact resources and the single approved source `/32`. The
   maintained image defaults to `127.0.0.1/32`, which permits no useful remote
   ingress when a source is not supplied.
2. Build and verify two Development Images: `tutorial-cloud-guard` from this
   directory and `lemc-gcp-terraform-vm` from the maintained Cookbook context.
3. Create `GOOGLE_CREDENTIALS` in **User > Secrets** through the write-only
   value field. The Cookbook contains only its name.
4. Create the lesson Cookbook and bind every guard and cloud step to its exact
   verified build. Create a private test App.
5. Run **Provision GCP VM** with `MACHINE_TYPE=e2-small`, set `SOURCE_CIDR` to
   the approved public IPv4 `/32`, and enter `CREATE`. Record the transition,
   Terraform output, generated resource prefix, project, region, zone, and the
   scheduled one-hour destroy task.
6. As soon as validation is complete, run **Destroy GCP VM** and enter
   `DESTROY`. Do not wait for the one-hour fallback.
7. Confirm Terraform destroy reaches terminal success. In GCP, query the exact
   recorded prefix and confirm that no VM, VPC, subnetwork, firewall rule, disk,
   address, or pending operation from the lesson remains.

## LEMCSSH

LEMCSSH can inspect secret metadata and submit the reviewed actions. Keep the
credential value in bounded stdin during the User Secret setup from Tutorial
05; never put it in these commands.

```sh
ssh LEMC_ALIAS auth secrets show GOOGLE_CREDENTIALS --format=json

ssh LEMC_ALIAS jobs run app --uuid APP_UUID --page 1 \
  --scope individual --recipe provision-gcp-vm \
  --set MACHINE_TYPE=e2-small --set SOURCE_CIDR=APPROVED_IPV4/32 \
  --set CONFIRM_CREATE=CREATE \
  --idempotency-key REQUEST_UUID --format=json

ssh LEMC_ALIAS jobs follow --transition-id TRANSITION_ID --render plain

ssh LEMC_ALIAS jobs status app --uuid APP_UUID --page 1 \
  --scope individual --transition-id TRANSITION_ID --format=json

ssh LEMC_ALIAS jobs run app --uuid APP_UUID --page 1 \
  --scope individual --recipe destroy-gcp-vm \
  --set CONFIRM_DESTROY=DESTROY \
  --idempotency-key REQUEST_UUID --format=json
```

## Expected result

Provision reaches terminal success, reports the exact resource prefix and VM
identity, and leaves one eligible one-hour IN teardown. Explicit destroy then
reaches terminal success and replaces or cancels the pending teardown. GCP
inventory for the recorded prefix is empty. The firewall inventory shows only
the submitted `/32` as its ingress source. A successful job without the GCP
inventory check is not complete teardown evidence.

## Cleanup and retry

Run explicit destroy even if provision reports a partial failure. Read exact
transition and Terraform state before retrying an unknown result. Reuse the
same idempotency key only for the identical request. If LEMC destroy cannot
finish, stop new work and use the project's approved operator process against
the recorded prefix. Preserve redacted evidence of each remaining resource.

After inventory is empty, disable the disposable GCP User Secret, remove the
App, and archive both Development Images and the Cookbook. Confirm no IN or
EVERY job remains eligible.

## Security and cost

This flow sends trusted recipe code the plaintext service-account JSON. Exact
redaction cannot stop transformed disclosure. Use a disposable project,
short-lived credential, least authority, budget alert, and the smallest
allowed machine. Supply only the operator's approved public IPv4 `/32`; do not
widen it to `0.0.0.0/0`. The one-hour schedule limits exposure but is not a
substitute for explicit teardown proof.

## Troubleshooting

- A guard exit code 64 means the confirmation word did not match. No later
  cloud step should start.
- The same guard exit code rejects a missing, malformed, or broader-than-`/32`
  IPv4 source prefix.
- Missing-secret preflight means no guest started. Configure the exact original
  author's User Secret; do not add credentials to YAML.
- `permission denied` from GCP means the dedicated service account lacks a
  required reviewed permission. Do not widen it to project owner.
- A Terraform timeout is not proof of rollback. Inspect the project by exact
  prefix and run the destroy path.
- A terminal destroy job with remaining resources is incomplete cleanup.
  Escalate through the authorized project process and keep the App archived.

## Internal validation

Maintainers run `./scripts/validate.sh` for static source, schema, and guard
checks. Cloud creation requires a separately authorized live gate and is never
part of the default command.
