Initializing Enclave...

How to Fix Terraform Plan Hanging Forever During AWS Provider Initialization on M1 Mac (Rosetta Issue)

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 10–20 mins


TL;DR

  • What broke: terraform plan or terraform init hangs at Initializing provider plugins... because Terraform is pulling or executing the darwin_amd64 (x86_64) AWS provider binary under Rosetta 2 instead of the native darwin_arm64 build, causing a silent deadlock in the emulation layer.
  • How to fix it: Force native arm64 binary resolution by unsetting Rosetta-inherited env vars, explicitly setting GOARCH=arm64, and pinning the provider to a version with confirmed arm64 support (>= 4.x).
  • Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your required_providers block and get the corrected config without sending your AWS credentials anywhere.

The Incident (What Does the Error Mean?)

The hang produces no error. That's the trap. Your terminal sits at:

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 3.x"...
- Installing hashicorp/aws v3.74.3...

...and then nothing. No timeout. No crash. CPU usage on the terraform process spikes to ~100% on a single core and stays there. Killing it with Ctrl+C and re-running produces the same result.

What is actually happening: The Terraform binary you installed (likely via Homebrew's default bottle or a pre-4.x installer) is a darwin_amd64 build running under Rosetta 2. When it shells out to execute the downloaded AWS provider plugin — which may itself be darwin_amd64 — the Rosetta translation layer encounters a lock contention or syscall emulation bug (specifically around fork/exec chains in Go runtime thread management). The process never terminates. On provider versions < 4.0, HashiCorp did not publish darwin_arm64 builds, compounding the issue.


The Attack Vector / Blast Radius

This is not a security vulnerability but the blast radius on a team is severe:

  • CI/CD pipelines unblocked locally but broken on dev machines. Engineers assume the hang is a network issue and start bypassing terraform plan gating entirely — shipping unreviewed infra changes.
  • Stale .terraform.lock.hcl files committed with darwin_amd64 hashes propagate to the entire team. Every M1 engineer on the team hits this. Linux CI passes; local dev is dead.
  • Silent state corruption risk: Engineers who force-kill a hung terraform plan mid-execution against a remote backend with state locking (DynamoDB) can leave the state lock acquired, blocking all subsequent runs until manual lock release via terraform force-unlock.
  • Version pinning debt: Teams that "fix" this by downgrading to an older provider version to avoid the hang introduce known CVEs in the AWS provider (e.g., older versions lack IMDSv2 enforcement).

How to Fix It (The Solution)

Step 1: Verify the Architecture Mismatch

# Check what architecture your terraform binary is
file $(which terraform)
# BAD output: Mach-O 64-bit executable x86_64  ← running under Rosetta
# GOOD output: Mach-O 64-bit executable arm64

# Check Rosetta env inheritance
echo $GOARCH
echo $GOFLAGS

Basic Fix — Reinstall Native arm64 Terraform and Purge the Plugin Cache

# 1. Uninstall the Rosetta-tainted binary
brew uninstall terraform

# 2. Ensure Homebrew itself is native arm64 (critical)
file $(which brew)
# Must show arm64. If not: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 3. Reinstall Terraform natively
brew install terraform

# 4. Purge the corrupted provider cache
rm -rf ~/.terraform.d/plugins
rm -rf .terraform
rm .terraform.lock.hcl

# 5. Unset any Rosetta-inherited Go env vars in your shell profile
unset GOARCH
unset GOFLAGS

# 6. Re-init
terraform init

Enterprise Best Practice — Pin Provider Version + Enforce arm64 via CLI Config

The problem in your versions.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
-     version = "~> 3.74"
+     version = "~> 5.0"
    }
  }
- required_version = ">= 0.13"
+ required_version = ">= 1.6.0"
}

Force native platform in .terraformrc (team-wide, commit this):

# ~/.terraformrc  (or $TF_CLI_CONFIG_FILE)
+provider_installation {
+  direct {
+    include = ["registry.terraform.io/*/*"]
+  }
+}

Add to your shell profile (~/.zshrc or ~/.bash_profile) to prevent Rosetta env bleed:

-# (nothing — env vars inherited from Rosetta terminal session)
+export GOARCH=arm64
+export GOOS=darwin
+# Ensure native arm64 Homebrew path takes precedence
+export PATH="/opt/homebrew/bin:$PATH"

Lock the platform in your .terraform.lock.hcl explicitly after init:

terraform providers lock \
  -platform=darwin_arm64 \
  -platform=linux_amd64 \
  -platform=linux_arm64

Commit the updated .terraform.lock.hcl. This forces all platforms to be resolved at lock time, preventing darwin_amd64 hashes from being recorded.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Enforce Provider Version Floors in Pre-Commit

Use tflint with the AWS ruleset:

# .tflint.hcl
plugin "aws" {
  enabled = true
  version = "0.27.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

Add to .pre-commit-config.yaml:

- repo: https://github.com/antonbabenko/pre-commit-terraform
  rev: v1.83.5
  hooks:
    - id: terraform_tflint
    - id: terraform_providers_lock
      args:
        - --hook-args=--platform=darwin_arm64 --platform=linux_amd64

2. CI Pipeline Guard (GitHub Actions)

- name: Verify Terraform provider lock includes arm64
  run: |
    grep -q 'darwin_arm64' .terraform.lock.hcl || \
      (echo "ERROR: .terraform.lock.hcl missing darwin_arm64 platform hash. Run: terraform providers lock -platform=darwin_arm64" && exit 1)

3. OPA Policy — Block Sub-4.x AWS Provider in Atlantis/TFC

package terraform.providers

deny[msg] {
  provider := input.configuration.provider_calls[_]
  provider.name == "aws"
  semver.compare(provider.version_constraint, "4.0.0") < 0
  msg := sprintf("AWS provider version '%v' is below 4.0.0. darwin_arm64 support requires >= 4.x.", [provider.version_constraint])
}

4. Standardize Dev Environments

Use mise (formerly rtx) or asdf with a .tool-versions file committed to the repo:

terraform 1.8.5

This eliminates the "works on my machine" Homebrew version drift that causes architecture mismatches across the team.

Related Diagnostics

"Part of the Performance Utility Matrix."

View all 219 Performance Tools →