A Dry Run Deployment Plan Belongs in Every PaaS
The Helm7 team · Published 27 September 2026 · Checked 30 September 2026 · 8 min read

Every button on most hosting dashboards acts the moment you press it, and you find out what it did from the invoice or the outage. Infrastructure tools solved this years ago with a plan you read before anything changes. A dry run deployment plan, with a price and a list of what cannot be undone, should be standard on a PaaS, and here is what it should contain.
In short
- A plan separates deciding from doing. Terraform documents that its plan command alone does not carry out the proposed changes.
- A useful plan lists each change, the monthly cost with its largest driver, and a plain sentence for anything irreversible.
- On Helm7 every operation that changes infrastructure takes dry_run: true, and destructive calls refuse without confirm: true while returning the plan.
- Use idempotency_key on anything that creates or spends, so a retry returns the original result.
- A cost figure should be a range. Cost estimates are estimates, and the vendors say so.
What does terraform plan actually do?
Terraform's documentation describes three steps. It reads the current state of the real objects and updates its records. It compares your configuration with that state. Then it proposes the set of actions that would make them match.
The important property is what it does not do.
“The plan command alone does not actually carry out the proposed changes.”
Output marks each action with a symbol: plus for create, tilde for update, minus for destroy, and minus-plus for a resource that must be destroyed and rebuilt. That last one is where people get hurt, because "update" and "replace" look similar until the data is gone. You can also save a plan with the out flag and apply exactly that file later. Terraform's tutorial notes that applying a saved plan "will not prompt you for approval and instead immediately execute the changes", so the approval has to happen earlier, at review time.
That is the whole trick behind any dry run deployment plan. Separate deciding from doing, and make the thing you decide on the same thing that gets done.
Why platforms skip the dry run deployment plan
A hosting dashboard optimises for the first deploy. Fewer steps means more people finish, so the safety step reads as friction. There is also a technical excuse: a plan needs a model of what exists and what would change, and it is easier to bolt an action onto a button than to build that model.
Both reasons made sense when a person clicked every button. They fall apart when a script or an AI agent is calling the API, because a script does not feel a dialog's hesitation. It needs the information in the response.
Guards exist elsewhere, and they are worth knowing about. Terraform has a prevent_destroy setting that rejects any plan that would destroy an object, but its documentation is clear that it does not stop Terraform destroying a resource if you remove its configuration. A guard that can be edited away is a speed bump. A plan you have to read is harder to skip.
What a good dry run deployment plan contains
Four things, in our view. Miss one and the dry run deployment plan is decoration.
- The ordered changes, each one a create, an update or a delete, with the name of the thing. Not "3 changes", the changes.
- The monthly cost of the result, as a range with the largest driver named, so a reader knows which line to argue with.
- A plain sentence for anything that cannot be undone, phrased as loss rather than as an operation. "The database and everything in it" stops a reader. "DELETE database" does not.
- A promise that it applied nothing, stated in the response, so a script cannot mistake a preview for a result.
| Preview | Changes listed | Cost shown | Irreversible loss called out |
|---|---|---|---|
| terraform plan | Yes, marked +, ~, - and -/+ | No, not by the CLI alone | Marked with a minus symbol, no sentence |
| HCP Terraform run | Yes | Estimate for many AWS, GCP and Azure resources, with monthly delta | As the plan |
| Helm7 dry_run: true | Yes, as ordered create, update and delete entries | Monthly band with the largest driver named | A plain sentence per destructive action |
Cost is the one most tools leave out, and it is the one people notice on the invoice. Terraform's hosted product does offer it for some clouds. HCP Terraform's docs say it shows an hourly and monthly cost for many resources, with the monthly delta, and it comes with a fair warning.
“Note that this is just an estimate; some resources don't have cost information available or have unpredictable usage-based pricing.”
Honesty about the estimate is part of the design. A single confident number is worse than a range, because the reader stops trusting the next one the first time it is wrong.
How Helm7 does it
On Helm7, every operation that changes infrastructure takes dry_run set to true. The API reference counts how many do, and the number is generated from the operation catalogue, so it cannot drift. A dry run deployment plan on Helm7 returns the ordered changes, a monthly cost band, the largest cost by name and a plain list of anything irreversible. It applies nothing and costs nothing.
A separate cost_estimate call prices a shape you have not built. Give it the resources you are considering and it returns a monthly figure with the main driver named. That covers the "which of these should I build" question before there is anything to plan.
Destructive calls work differently. Without confirm set to true, they refuse with the code confirmation_required, and the refusal carries the same plan a dry run would have returned. Someone who always plans first never meets a surprise, and someone who forgets still gets the information. On anything that creates or spends, an idempotency_key means a retried request returns the original result instead of acting twice.
The same operations are exposed to AI agents as tools over MCP, so an agent sees exactly the behaviour a person calling the API does. The agents page walks through a session where a $5 key is refused a machine it cannot afford. That is the point of the design: the limit is enforced before the money moves.
A worked dry run deployment plan: restoring a database
A restore is the sharpest test, because it is not a deletion but it loses data all the same. Everything written after the restore point disappears. The ids below are illustrative, and the plan is abridged to the fields that matter. The wording of the destructive line is what the platform's plan code produces for a restore.
database_restore
{
"productId": "prd_7k2m9x4qw1nv",
"databaseId": "db_example",
"point_in_time": "2026-09-26T09:00:00Z",
"dry_run": true
}{
"changes": [
{ "action": "update", "resource": "database-restore", "name": "primary", "destructive": true }
],
"destructive_actions": [
"Everything written to primary after the restore point. The restore replaces the current contents, and rows newer than it cannot be recovered afterwards."
],
"applied": false
}Read the destructive line. It says "everything written after the restore point", and that is the sentence a person checks the timestamp against. If the timestamp is wrong, the mistake is caught here. If it is right, the same call is sent again with confirm set to true. Calling without confirm gets you the refusal and the same plan, so skipping the dry run costs you nothing worse than one extra round trip.
The catalogue also offers a safer route for the unsure. Restoring into a new database leaves the current one alone, and the operation description says it is strongly preferred when you are not certain.
What a dry run deployment plan cannot promise
A dry run deployment plan is a prediction, not a guarantee. Usage-based costs cannot be known in advance, which is why the figure is a band. State can change between the plan and the apply, so a plan taken an hour ago may describe a world that has moved. Any saved plan has the same weakness, so treat an old one as a draft.
A plan also does not replace backups. It stops you doing something by accident. It does not undo something done on purpose. If you are weighing infrastructure tools rather than platforms, our Terraform and Pulumi comparison covers the plan step in both. And for the keys that make the calls, see scoped credentials for AI agents.
Helm7 is new and runs in two EU regions. If you need a preview across several clouds, a full infrastructure as code tool is still the right answer. The claim here is narrower: a platform that changes real things should show the change first.
Common questions
What is a dry run in deployment?
A dry run works out what a change would do and reports it without applying anything. A good one lists the changes, the cost and anything that cannot be undone.
Is a dry run the same as terraform plan?
The idea is the same. Terraform plan reads current state, compares it with your configuration and proposes actions without carrying them out. A platform dry run does the same for an API call rather than a configuration file.
How does Helm7 confirm destructive actions?
Destructive operations refuse to run unless confirm is set to true. The refusal returns the same plan a dry run would, listing what would be lost, so the caller can decide with full information.
Can I see the cost before I deploy?
Yes. Every dry run includes a monthly cost band, and the cost_estimate operation prices a set of resources you have not created yet, naming the largest cost driver.