New to Kestra?
Use blueprints to kickstart your first workflows.
Spin up an OpenTofu preview stack, run tests, and destroy it in a Kestra finally block so ephemeral environments never leak, with Slack alerts on failure.
Preview environments leak when the teardown lives in a script that only runs on success. This blueprint makes destruction structural: io.kestra.plugin.opentofu.cli.OpenTofuCLI applies a preview stack with the execution ID baked into resource names, the test placeholder runs against it, and the destroy sits in Kestra's finally block, which executes whether the tests passed, failed, or something between them crashed. The destroy consumes the exact state file the apply produced, handed over through internal storage, so it tears down precisely this execution's stack and nothing else. The same guarantee this flow gives a local demo stack applies to real cloud previews, such as the ephemeral droplet pattern on DigitalOcean.
apply_preview receives main.tf through inputFiles and the execution ID through env as TF_VAR_execution_id, so every resource name is unique per run and two concurrent previews cannot collide. outputFiles uploads terraform.tfstate to internal storage.run_e2e_tests is a Log placeholder standing in for your real test suite, whether that is a container running Playwright, a shell task running k6, or a subflow.destroy_preview lives in the finally block. It receives the same configuration plus the state file from the apply task's outputs, reinitializes the provider, and runs tofu destroy -auto-approve. Kestra runs finally tasks regardless of what happened upstream.errors block posts to Slack on any failure and explicitly tells the responder to verify the teardown, covering the rare case where the apply itself broke before producing state.local_file provider.OpenTofu can create and destroy a stack, but nothing in OpenTofu guarantees the destroy runs after a failed test suite. Kestra's finally block is that guarantee, and internal storage is the thread that connects the two invocations: the state travels from apply to destroy as a typed artifact of this execution, visible in the UI, never parked in a shared backend where a concurrent run could touch it.
env from {{ secret('...') }} in both OpenTofu tasks.SLACK_WEBHOOK_URL: Slack incoming webhook URL.SLACK_WEBHOOK_URL secret to your Kestra namespace.main.tf in both tasks with your real preview stack and swap the Log placeholder for your test runner.TF_VAR variables alongside the execution ID.Pause before the finally teardown for manual exploration windows.