A Safe Team Workflow
Scenario: Two separate pull requests both pass their CI plan and both look completely safe in isolation, but merging and applying the first one silently invalidates the second one's already-approved plan.
This module pulls together nearly everything you've learned so far into one coherent team process. A safe collaborative workflow needs, at minimum: a shared remote backend with locking (Module 5), version-controlled configuration and a locked-down dependency lock file (Modules 2-3), a single consistent runner rather than everyone's individual laptops, clear identity per run (Module 3), a genuine human or policy review step on every plan, and serialized applies so two changes can't land on top of each other.
The scenario above illustrates something worth internalizing precisely: a plan is a snapshot in time. The moment real infrastructure changes underneath an already-calculated plan - because someone else's change merged and applied first - that plan may no longer accurately describe what applying it will actually do. A robust CI pipeline handles this by formatting and validating first, initializing deterministically, planning with -detailed-exitcode (which distinguishes "no changes" from "changes proposed" from "error" using different exit codes, useful for automation to branch on), publishing the full diff somewhere reviewers can see it securely, requiring the right level of approval for the environment being touched, and only then applying - ideally the exact reviewed plan artifact, not a freshly recalculated one that might have silently drifted from what was approved.
Analogy: A flight checklist plus an air traffic control tower coordinate even highly skilled, experienced pilots. Individual skill alone does not prevent two aircraft from trying to use the same runway at the same moment - that requires an external coordinating system everyone defers to, which is exactly the role locking and serialized applies play for a Terraform team.
A worked configuration
terraform fmt -check -recursive
terraform init -input=false
terraform validate
terraform plan -detailed-exitcode -out=change.tfplan
In practice, use the exit code from -detailed-exitcode to programmatically distinguish "nothing to do" from "a real change is proposed" from "something actually failed," rather than parsing plan text. Restrict who can read plan output and saved plan artifacts (they can contain sensitive values, as you learned in Module 4), configure your pipeline to cancel a superseded run rather than let two stale runs race each other to apply, and make sure exactly one system - not several well-meaning individuals - is ever authorized to apply against any given workspace.
Common mistakes in production
- Applying a plan artifact that was calculated before another change merged and applied, without re-planning to confirm it's still accurate - the plan is a snapshot, not a living guarantee.
- Letting more than one system or person have apply authority on the same workspace "just in case" - this is exactly the condition that makes two concurrent applies possible in the first place.
Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-remote-state-locking Terraform lab. Open/labs/terraformand choose slugtf-remote-state-locking; the lab runs real Terraform against the offline FakeCloud provider.