Provider Identity and Least Privilege

Scenario: A team gives Terraform an organization-wide administrator credential, reasoning that since it might eventually need to create almost any kind of resource, it's simpler to just grant it everything up front.

This is one of the most common real-world Terraform security mistakes, precisely because it feels like the path of least friction in the moment. The actual production-safe approach is to split states and workspaces by ownership and permission boundary (echoing Module 6's directory-per-environment reasoning) so that any one runner only ever needs a genuinely narrow, specific set of permissions - a workspace that only ever touches DNS records has no legitimate reason to hold credentials that could also delete a production database.

Wherever the platform supports it, prefer workload identity or HCP Terraform's dynamic credentials: rather than a long-lived static key sitting in a secrets store waiting to be leaked, the runner is issued a short-lived, run-scoped token right at the moment it actually needs one, which expires shortly after. Where a provider allows it, separate the permissions needed to merely plan (largely read access) from those needed to actually apply (write/create/destroy) - a compromised or misconfigured planning stage then has no destructive capability even if something goes wrong. And be disciplined about provider aliases (Module 3): they should model a genuine, deliberate multi-account or multi-region topology, never become a casual shortcut for one state to reach across every account boundary a company has.

Analogy: A single master key that opens every door in a building is wonderfully convenient right up until it's lost or stolen - at which point every single room is compromised at once. Separate keyed rooms with individually time-limited access badges are more work to set up, but they constrain the damage from both an honest mistake and a deliberate attacker to just the one room the badge was actually issued for.

A worked configuration

# Credentials are injected by the runner; configuration contains no key.
provider "fakecloud" {
  endpoint = var.endpoint
}

Build the ability to audit any provider-side API call all the way back to the specific Terraform run - and ideally the specific human approval - that caused it; without this, incident response after a bad apply becomes pure guesswork about who did what. Rotate any long-lived credential that does still exist on a regular schedule, alert on any provider-side change that didn't come through an audited Terraform run at all (a strong drift-and-security signal together), and structurally ensure a compromised or misconfigured development pipeline has no technical path to assume production-level permissions, regardless of what policy says it should or shouldn't do.

Common mistakes in production

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/terraform and choose slug tf-remote-state-locking; the lab runs real Terraform against the offline FakeCloud provider.