# Terraform Plan Failed in CI
Common Error Patterns
Terraform plan failures typically show:
Error: Error refreshing state: state lock errorError: Failed to get existing workspaces: AccessDeniedError: Provider produced inconsistent result after applyError: required_providers block must be specifiedError: Error running plan: 1 error(s) occurredRoot Causes and Solutions
1. State Lock Conflict
State is locked by another Terraform operation.
Solution:
Check for lock:
terraform force-unlock LOCK_IDFor S3 backend with DynamoDB lock table:
```bash # Check DynamoDB lock aws dynamodb scan --table-name terraform-locks
# Delete lock manually (use with caution) aws dynamodb delete-item \ --table-name terraform-locks \ --key '{"LockID":{"S":"terraform-state-lock"}}' ```
In CI, use retry mechanism:
# GitHub Actions with retry
- name: Terraform Plan
uses: nick-fields/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: terraform plan -out=tfplan2. Backend Authentication Failure
Cannot access state backend storage.
Solution:
For S3 backend:
```yaml # GitHub Actions - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/terraform-role aws-region: us-east-1
- name: Terraform Init
- run: terraform init
`
For Terraform Cloud:
- name: Terraform Init
env:
TF_TOKEN_app_terraform_io: ${{ secrets.TFC_TOKEN }}
run: terraform initFor GCS backend:
```yaml - name: Authenticate to Google Cloud uses: google-github-actions/auth@v2 with: credentials_json: ${{ secrets.GCP_CREDENTIALS }}
- name: Terraform Init
- run: terraform init
`
3. Provider Configuration Errors
Provider not properly configured.
Solution:
Use required_providers block:
```hcl terraform { required_version = ">= 1.0"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } google = { source = "hashicorp/google" version = "~> 4.0" } } }
provider "aws" { region = var.aws_region } ```
For multiple provider configurations:
```hcl provider "aws" { alias = "east" region = "us-east-1" }
provider "aws" { alias = "west" region = "us-west-2" }
resource "aws_s3_bucket" "east_bucket" { provider = aws.east bucket = "east-bucket" } ```
4. Missing Variables
Required variables not provided.
Solution:
Pass variables via CLI:
terraform plan \
-var="aws_region=us-east-1" \
-var="environment=production" \
-var-file="production.tfvars"Or use environment variables:
export TF_VAR_aws_region="us-east-1"
export TF_VAR_environment="production"
terraform planIn CI:
- name: Terraform Plan
env:
TF_VAR_aws_region: us-east-1
TF_VAR_environment: production
run: |
terraform plan \
-var-file="environments/${{ github.ref_name }}.tfvars" \
-out=tfplan5. Invalid Configuration Syntax
HCL syntax errors in configuration.
Solution:
Validate configuration:
terraform fmt -check
terraform validateIn CI pipeline:
```yaml - name: Terraform Format Check run: terraform fmt -check -recursive
- name: Terraform Validate
- run: terraform validate
`
Use terraform-validator:
- name: Check Terraform
uses: tf-actions/tf-validate@v1
with:
path: terraform/6. State Drift
Actual infrastructure differs from Terraform state.
Solution:
Refresh state before plan:
terraform refresh
terraform planOr use -refresh-only:
terraform plan -refresh-onlyDetect drift:
# Show state vs actual
terraform plan -detailed-exitcode
# Exit code:
# 0 = no changes
# 1 = error
# 2 = changes presentImport existing resources:
terraform import aws_s3_bucket.mybucket mybucket-name7. Module Download Failures
Cannot download modules from registry.
Solution:
Use module caching:
terraform init -get=false
terraform init -plugin-dir=/pluginsFor private modules:
module "private_module" {
source = "app.terraform.io/organization/module/aws"
version = "1.0.0"
}Configure Terraform Cloud token:
- name: Terraform Init
env:
TF_TOKEN_app_terraform_io: ${{ secrets.TFC_TOKEN }}
run: terraform init8. Workspace/Environment Issues
Wrong workspace selected.
Solution:
Create and select workspace:
terraform workspace new production
terraform workspace select production
terraform planIn CI:
- name: Terraform Workspace
run: |
terraform workspace select ${{ github.ref_name }} || \
terraform workspace new ${{ github.ref_name }}Use workspace-specific state:
terraform {
backend "s3" {
bucket = "terraform-state"
key = "terraform.tfstate"
dynamodb_table = "terraform-locks"
region = "us-east-1"
workspace_key_prefix = "environments"
}
}CI Pipeline Configuration
GitHub Actions Full Workflow
```yaml name: Terraform
on: push: branches: [main] pull_request: branches: [main]
jobs: terraform: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - name: Checkout uses: actions/checkout@v4
- name: Setup Terraform
- uses: hashicorp/setup-terraform@v3
- with:
- terraform_version: 1.5.0
- name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v4
- with:
- role-to-assume: arn:aws:iam::123456789012:role/terraform-role
- aws-region: us-east-1
- name: Terraform Format
- run: terraform fmt -check
- name: Terraform Init
- run: terraform init
- name: Terraform Validate
- run: terraform validate
- name: Terraform Plan
- run: terraform plan -out=tfplan
- name: Terraform Apply
- if: github.ref == 'refs/heads/main' && github.event_name == 'push'
- run: terraform apply -auto-approve tfplan
`
GitLab CI Configuration
```yaml stages: - validate - plan - apply
variables: TF_IN_AUTOMATION: "true" TF_ROOT: "terraform"
validate: stage: validate image: hashicorp/terraform:1.5 script: - terraform fmt -check - terraform init - terraform validate
plan: stage: plan image: hashicorp/terraform:1.5 script: - terraform init - terraform plan -out=tfplan artifacts: paths: - tfplan
apply: stage: apply image: hashicorp/terraform:1.5 script: - terraform init - terraform apply -auto-approve tfplan rules: - if: $CI_COMMIT_BRANCH == "main" dependencies: - plan ```
Debugging Commands
```bash # Verbose plan output terraform plan -out=tfplan -verbose
# Show plan details terraform show tfplan
# Debug mode TF_LOG=DEBUG terraform plan
# Trace mode TF_LOG=TRACE terraform plan 2>&1 | tee terraform-debug.log
# Check state terraform state list terraform state show aws_s3_bucket.main
# State manipulation terraform state mv aws_s3_bucket.old aws_s3_bucket.new terraform state rm aws_s3_bucket.unmanaged ```
Quick Reference
| Error | Solution |
|---|---|
| State lock | terraform force-unlock |
| Auth failed | Configure provider credentials |
| Missing vars | Pass -var or TF_VAR_* |
| Syntax error | Run terraform fmt and validate |
| Module fail | Use token for private modules |
| Drift | Run terraform refresh |
Prevention Tips
- 1.Use
terraform fmtandvalidatein CI - 2.Configure proper state locking
- 3.Use OIDC authentication for cloud providers
- 4.Run plan on every PR before apply
- 5.Store plan artifacts for audit
- 6.Use sentinel/policy-as-code for governance
Related Articles
- [AWS CloudFormation Stack Failed](#)
- [Kubernetes Deployment Failed in CI](#)
- [AWS IAM Permission Denied](#)