Skip to main content
Use this guide when you already manage infrastructure with Terraform or OpenTofu and want Ravion to run it. You keep your repository, your .tf files, and your state — Ravion becomes the place where plans and applies run, get reviewed, and get approved. Nothing is imported or recreated. An rvn-stack module points at your repo and your existing backend. The first plan reads your current state; if your infrastructure has no drift, it shows no changes.
Have resources that aren’t in any Terraform state yet? Set up the stack with this guide first, then follow Import into a Terraform Stack to adopt them.

What you get

Every rvn-stack module runs your organization’s change pipeline — terraform:plan → approval → terraform:apply — on temporary EC2 runners in your AWS account. Your code, variables, and credentials never enter the Ravion control plane. Two independent inputs decide what each git event runs. Each accepts none, plan, or plan_and_apply:
  • plan runs terraform:plan and finishes. Nothing is applied. Review the plan in the Ravion run view or with the CLI; posting it to the PR as a comment and commit check is coming soon.
  • plan_and_apply runs the full pipeline and stops at the approval gate. Only runs you approve apply, and the apply uses exactly the plan file you reviewed.
  • none doesn’t start a run for that event.
Manual runs (ravion stack trigger-pipeline, or Plan / Apply / Destroy in the dashboard) always target the primary branch and aren’t affected by these inputs.

Pick your git workflow

The default. Pull requests are plan only; nothing applies until the change is merged. Merging pushes to the primary branch, which runs a fresh plan against the merged commit and current state, then waits for approval.
To skip the post-merge approval click, set primary_branch_autoapprove_enabled: true. Plans that destroy or replace resources still stop for approval.
pull_request_runs and primary_branch_runs require rvn-stack 1.3.0 or later. Earlier versions run plan → approve → apply on every push and every pull request.

Before you start

Your state stays where it is. When you create the module, set use_ravion_state_backend to false so Ravion uses the backend block already in your configuration (S3 + DynamoDB, Terraform Cloud, and so on). This input is immutable after creation, so get it right the first time.Leave use_ravion_state_backend at its default (true) only for a new configuration that declares an empty terraform { cloud {} } block — Ravion then hosts state for you. Turning it on for an existing configuration would start from empty state and plan to create everything.
Plan and apply run on an EC2 instance in the AWS account you choose for the module, with an IAM role Ravion creates for the run. Plan steps get a read-only policy set (plus S3, DynamoDB, and Secrets Manager access for backends and data sources); apply steps get AdministratorAccess.Make sure your provider and backend blocks work with the default credential chain — remove hardcoded profile, access_key, or shared_credentials_files settings. If your provider uses assume_role into another account, that role’s trust policy must allow the runner account. If your state bucket is encrypted with a customer-managed KMS key, the plan role needs kms:Decrypt on that key.
Anything you pass on the command line today needs a home in the module:
  • -var-file flags → terraform_variable_files (paths relative to base_path)
  • -var flags → tf_variables
  • Environment variables, including TF_VAR_* and provider tokens → env_variables, with from_secrets_manager or from_parameter_store for secret values

Use an agent

The steps are the manual walkthrough below — copy this prompt into your coding agent in the repository containing your Terraform code:

Connect this repo's Terraform to Ravion and run the first plan.

Open in Cursor

Manual walkthrough

1

Add an rvn-stack module to your project config

In your project’s ravion.yaml, add one module instance per Terraform root module (one per environment is typical). Point repo and base_path at the directory that holds your .tf files. The highlighted lines are the ones that matter for an existing stack — keep your state backend, and choose where applies happen:
ravion.yaml
watch_paths defaults to <base_path>/**, so changes outside the root module directory don’t trigger runs. Add shared module directories if your root module references them:
See the rvn-stack inputs reference for every field. If you don’t have a project yet, ravion project config apply creates it — see Project config file.
2

Apply the config and run the first plan

Creating a stack module starts a change run automatically:
The apply prints a PIPELINE_RUN_ID for the run it started. Wait for it to reach the approval gate and read the plan:
You can also create the module from the dashboard, or with ravion module create --initial-stack-run PLAN.
3

Confirm the plan matches what you expect

With no drift, the plan reports no changes and the run finishes without an approval gate. Every resource in your state now appears under the stack’s resources in the dashboard.If the plan shows changes, stop and read them before approving anything:Fix the input or configuration, push, and the next run replans.
4

Open a pull request

Push a branch that changes something under watch_paths and open a PR against main. With pull_request_runs: plan, Ravion plans the branch and nothing applies. The run appears on the stack’s Runs tab in the dashboard; posting the summary back to the PR as a comment and commit check is coming soon.Read the full plan from the run, or from the CLI:
With pull_request_runs: plan_and_apply, the same run also stops at an approval gate; approving it applies the PR branch. Only do this if you chose the apply-from-PR workflow.
5

Merge, then approve the primary branch run

Merging pushes to main. With primary_branch_runs: plan_and_apply, Ravion runs a fresh plan against the merged commit and your current state — it doesn’t reuse the PR plan, so it can’t be stale. When the run reaches the approval gate, review and approve in the dashboard or with the CLI:
The apply uses exactly the plan file you reviewed. Set primary_branch_autoapprove_enabled: true to skip this step for non-destructive plans.If you apply from PRs instead (primary_branch_runs: none), merging starts nothing — the branch was already applied.

Ad-hoc plans and drift checks

Trigger a run on the primary branch at any time, regardless of primary_branch_runs. A run with no changes finishes on its own; one with changes stops at the approval gate, so this doubles as a drift check:

Next steps

Import into a Terraform Stack

Adopt resources that exist in AWS but not in your state, using import blocks.

Terraform Stack module

Full rvn-stack input reference: triggers, tfvars, environment variables, state backend options.

Module stack

How change runs, approvals, and custom pipelines work.

Stuck state lock

Recover when a run is interrupted mid-apply.