Fix Terraform Error: CloudWatch Log Group Already Exists
Fix terraform CloudWatch Log Group ResourceAlreadyExistsException. Import orphaned log groups, prevent Lambda auto-creation
DevOps
Learn how to fix Google Cloud 403 Forbidden and insufficient permissions errors in Terraform. Covers IAM roles, service account setup, and API enablement.
The GCP 403 Forbidden error means the service account or user running Terraform lacks the IAM permissions or API enablement required for the requested operation. Grant the correct IAM roles, enable the required APIs, or authenticate with a properly-configured service account.
When running terraform plan or terraform apply against Google Cloud, you encounter one of these errors:
Error: googleapi: Error 403: Required permissions not availableError: googleapi: Error 403: Caller does not have permissionError: googleapi: Error 403: Access Not Configured.
[service] has not been used in project [PROJECT_ID] before
or it is disabled. Enable it by visiting
https://console.developers.google.com/apis/api/...Error: Error creating Instance: googleapi: Error 403:
Required 'compute.instances.create' permission for
'projects/my-project/zones/us-central1-a/instances/web-server'Google Cloud requires each service API to be explicitly enabled per project. If you try to create Compute Engine resources without enabling compute.googleapis.com, GCP returns 403.
The Terraform service account doesn't have the IAM role needed for the operation. For example, creating a GKE cluster requires roles/container.admin, not just roles/viewer.
Terraform is authenticating against project A but the resources belong to project B. The service account may have permissions in one project but not the other.
An organization-level policy may block certain operations (e.g., external IP creation, specific regions) regardless of IAM permissions.
The GOOGLE_APPLICATION_CREDENTIALS environment variable points to an expired or incorrect key file, or you're using Application Default Credentials from the wrong account.
Each GCP service requires its API to be enabled. Enable the most common ones:
# Compute Engine
gcloud services enable compute.googleapis.com --project=PROJECT_ID
# Kubernetes Engine
gcloud services enable container.googleapis.com --project=PROJECT_ID
# Cloud SQL
gcloud services enable sqladmin.googleapis.com --project=PROJECT_ID
# Cloud Storage
gcloud services enable storage.googleapis.com --project=PROJECT_ID
# IAM
gcloud services enable iam.googleapis.com --project=PROJECT_ID
# Cloud Resource Manager
gcloud services enable cloudresourcemanager.googleapis.com --project=PROJECT_IDYou can also enable APIs in Terraform itself:
resource "google_project_service" "compute" {
project = var.project_id
service = "compute.googleapis.com"
disable_on_destroy = false
}
resource "google_compute_instance" "web" {
depends_on = [google_project_service.compute]
# ...
}Identify what role the operation needs and grant it:
# Check current roles
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--filter="bindings.members:terraform@PROJECT_ID.iam.gserviceaccount.com" \
--format="table(bindings.role)"
# Grant Compute Admin
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/compute.admin"
# Grant Storage Admin
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.admin"
# Grant Kubernetes Admin
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/container.admin"Common role mappings:
| Resource Type | Required IAM Role |
|---|---|
| Compute instances | roles/compute.admin |
| GKE clusters | roles/container.admin |
| Cloud SQL | roles/cloudsql.admin |
| Cloud Storage | roles/storage.admin |
| VPC networks | roles/compute.networkAdmin |
| IAM management | roles/iam.admin |
| DNS records | roles/dns.admin |
| Pub/Sub | roles/pubsub.admin |
# Create dedicated Terraform service account
gcloud iam service-accounts create terraform \
--display-name="Terraform Service Account" \
--project=PROJECT_ID
# Grant Editor role (broad — use specific roles in production)
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/editor"
# Create and download key
gcloud iam service-accounts keys create terraform-key.json \
--iam-account=terraform@PROJECT_ID.iam.gserviceaccount.com
# Set credential in environment
export GOOGLE_APPLICATION_CREDENTIALS="$(pwd)/terraform-key.json"Or reference it in the provider block:
provider "google" {
project = "my-project-id"
region = "us-central1"
credentials = file("terraform-key.json")
}Make sure Terraform is targeting the right project:
provider "google" {
project = "correct-project-id" # Verify this matches
region = "us-central1"
}Check which account is active:
# Check active gcloud account
gcloud auth list
# Check active project
gcloud config get-value project
# Verify service account identity
gcloud auth activate-service-account --key-file=terraform-key.json
gcloud projects get-iam-policy PROJECT_IDIf an org policy blocks the operation:
# List active org policies
gcloud org-policies list --project=PROJECT_ID
# Check specific constraint
gcloud org-policies describe compute.vmExternalIpAccess \
--project=PROJECT_IDWork within the constraint or request an exception from your org admin.
Enable detailed logging to see the exact permission being checked:
export TF_LOG=DEBUG
terraform plan 2>&1 | grep -i "403\|permission\|forbidden"Use the IAM Policy Troubleshooter in the GCP Console to check whether a specific principal has a specific permission on a specific resource.
gcloud services list --enabled)gcloud projects get-iam-policy)GOOGLE_APPLICATION_CREDENTIALS set to a valid key file?roles/compute.instanceAdmin rather than broad roles/editorgoogle_project_service with depends_on to avoid race conditionsREADME.md listing what IAM roles the Terraform config needsThe GCP 403 error in Terraform almost always comes down to missing IAM roles or disabled APIs. Check the error message carefully — it usually tells you exactly which permission or API is needed. Enable the API, grant the role, verify your credentials, and re-run terraform plan.
Fix terraform CloudWatch Log Group ResourceAlreadyExistsException. Import orphaned log groups, prevent Lambda auto-creation
Fix terraform import errors when a resource already exists in state. Covers state rm, state show, reimport workflow, import blocks
Fix terraform too many command line arguments errors. Correct -var syntax, quote values with spaces, and learn proper Terraform CLI argument format for plan
Fix terraform invalid escape sequence errors. Double backslashes for Windows paths, use heredocs for regex, and learn all valid HCL escape sequences.