OrchStep for regulated industries
In regulated shops the deploy isn't the hard part — proving who approved it and what shipped is. Here's how to make the change record a byproduct of running the change.
If you work where SOX, HIPAA, PCI, or an internal change-advisory board sets the rules, the technical deploy is the easy 10%. The other 90% is evidence: who approved this change, what artifact actually shipped, was the production gate enforced or just supposed to be, and can you produce all of that nine months later when an auditor asks. Most teams answer with screenshots, a ticket pasted into Slack, and a deploy script that has no idea any of this exists.
The problem with that split is drift. The runbook says "requires VP approval." The script doesn't check. One Friday it ships anyway. Now the control is a sentence in a doc instead of a thing the system enforces. OrchStep closes that gap by making the control a step — an assertion that fails the run if the approver isn't named, an audit line that's written whether the change succeeds or rolls back.
What automation looks like in a regulated shop
You're not optimizing for speed. You're optimizing for defensibility. That means:
- Controls that are enforced by the run, not documented next to it.
- An audit trail that's a byproduct of executing the change, not a manual second step someone forgets.
- A preview an approver or auditor can read before anything touches production.
- Separation between staging and production that the workflow guarantees, not convention.
Where OrchStep fits
OrchStep runs your existing deploy commands, but wraps them in assert gates, if: branches, and finally: blocks that always run. The approval check isn't a comment — it's a step that fails the workflow. The audit log isn't a follow-up task — it's a finally: that fires on success and on rollback alike.
Here's a controlled-change workflow where the compliance controls are part of the run:
name: controlled-change
defaults:
change_id: CHG-0001
tier: staging
approver: unset
tasks:
# orchstep run change --var change_id=CHG-2048 --var tier=production --var approver=jdoe
change:
steps:
- name: record
func: shell
do: echo "opening change {{ vars.change_id }} for {{ vars.tier }}"
outputs:
ticket: "{{ vars.change_id }}"
- name: require-approval
func: assert
args:
condition: '{{ ne vars.approver "unset" }}'
message: "production change needs a named approver"
- name: evidence
func: shell
do: echo "collecting build provenance for {{ steps.record.ticket }}"
- name: apply
func: shell
do: echo "applying {{ steps.record.ticket }} to {{ vars.tier }}"
catch:
- name: revert
func: shell
do: echo "reverting {{ steps.record.ticket }} on {{ vars.tier }}"
finally:
- name: audit
func: shell
do: echo "audit log sealed for {{ steps.record.ticket }} by {{ vars.approver }}"
- name: gate
if: '{{ eq vars.tier "production" }}'
then:
- name: attest
func: shell
do: echo "attestation filed for {{ steps.record.ticket }}"
else:
- name: stage-note
func: shell
do: echo "staged {{ steps.record.ticket }}, no attestation required"Run it without an approver and the require-approval assert stops the workflow before anything is applied — the control is enforced, not advisory. The audit step is in finally:, so the trail is written whether the change applies cleanly or reverts. The production attest step only runs on the production tier, and the workflow guarantees it.
Three workflows worth stealing
1. The approval gate that actually gates. The assert step turns "production changes require a named approver" from policy text into an enforced precondition. No approver, no run. Pair it with your CI's required-reviewers so the named approver in the run matches the one who clicked approve. See The assert function and the Compliance-gated change use case.
2. The audit trail as a byproduct. Everything the auditor wants — change ID, tier, approver, outcome — is emitted by steps and sealed in a finally: block that runs on success and on rollback. The evidence is produced by running the change, so there's no second manual step to skip. See Error Handling.
3. The pre-change preview. Before a CAB meeting, orchstep run change --var change_id=CHG-2048 --var tier=production --dry-run resolves every variable and prints the exact plan — which branch runs, what gets applied — without executing. The board reviews the real plan, not a description of it. See Previewing with Dry Run.
What you gained
| Concern | Script + ticket + screenshots | OrchStep |
|---|---|---|
| Approval enforcement | a sentence in the runbook | assert that fails the run |
| Audit trail | manual, easy to forget | finally: step, always runs |
| Prod vs staging controls | convention | if: gate the workflow guarantees |
| Rollback evidence | "we think we reverted" | catch: + sealed audit line |
| Pre-change review | a description | --dry-run of the real plan |
| Change record | assembled after the fact | a byproduct of the run |
OrchStep is not a GRC platform and doesn't replace your system of record — it makes the deploy produce clean evidence for whatever system you already use. If your controls are fully handled by an existing change-management tool with enforced gates, lean on it. Where the gap is "the script doesn't know about the policy," a workflow that encodes the control is what closes it. For sensitive values, keep them out of the workflow file entirely with the secrets conduit.
Where to go next
- Compliance-gated change — the full pattern end to end
- The assert function — controls that fail the run
- Error Handling — catch, finally, and guaranteed audit steps
- Secrets — keep regulated values out of the workflow file
Spend more time proving the change than making it? Make the change record fall out of running the change.
curl -fsSL https://orchstep.dev/install.sh | sh