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 JSON parsing invalid character errors in Terraform policies, IAM templates, and external data sources. Includes validation and debugging tips.
Your Terraform configuration contains malformed JSON — typically trailing commas, single quotes, unescaped characters, or a BOM marker. Use jsonencode() to generate JSON from HCL objects, or validate your JSON with jq or python3 -m json.tool before using it.
When running terraform plan or terraform apply, you encounter:
Error: "policy" contains an invalid JSON: invalid character '}' after object key:value pairError: Error parsing JSON: invalid character '\'' looking for beginning of valueError: "assume_role_policy" contains an invalid JSON:
invalid character ',' after object key:value pairError: Error in function call: invalid character 'S' looking for beginning of valueJSON does not allow trailing commas (unlike HCL or JavaScript):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "*", // ← trailing comma = INVALID
}, // ← trailing comma = INVALID
]
}JSON requires double quotes for strings:
{'Effect': 'Allow'} // ← INVALID — single quotes
{"Effect": "Allow"} // ← VALID — double quotesStrings containing backslashes, quotes, or newlines must be escaped:
{"path": "C:\Users\admin"} // ← INVALID
{"path": "C:\\Users\\admin"} // ← VALIDFiles saved with a BOM (common in Windows editors) have invisible characters at the start:
\xEF\xBB\xBF{"Version": "2012-10-17"...}JSON does not support comments:
{
// This is not allowed in JSON
"Version": "2012-10-17"
}Using ${...} incorrectly in JSON strings:
# BAD — unescaped interpolation inside JSON
policy = <<EOF
{"Resource": "arn:aws:s3:::${var.bucket}/*"}
EOFThe best approach — let Terraform generate valid JSON from HCL objects:
resource "aws_iam_policy" "s3_read" {
name = "s3-read-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowS3Read"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket",
]
Resource = [
"arn:aws:s3:::${var.bucket_name}",
"arn:aws:s3:::${var.bucket_name}/*",
]
}
]
})
}jsonencode() handles:
For AWS IAM policies specifically:
data "aws_iam_policy_document" "s3_read" {
statement {
sid = "AllowS3Read"
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket",
]
resources = [
"arn:aws:s3:::${var.bucket_name}",
"arn:aws:s3:::${var.bucket_name}/*",
]
}
}
resource "aws_iam_policy" "s3_read" {
name = "s3-read-policy"
policy = data.aws_iam_policy_document.s3_read.json
}This is more readable, supports conditions natively, and never produces invalid JSON.
If you must use external JSON files:
# Validate with jq
jq . policy.json
# Validate with Python
python3 -m json.tool policy.json
# Find the exact error location
python3 -c "
import json
try:
json.load(open('policy.json'))
print('Valid JSON')
except json.JSONDecodeError as e:
print(f'Error at line {e.lineno}, column {e.colno}: {e.msg}')
"Reference validated JSON files in Terraform:
resource "aws_iam_policy" "main" {
name = "my-policy"
policy = file("${path.module}/policies/s3-read.json")
}# Check for BOM
file policy.json
# Output: "policy.json: UTF-8 Unicode (with BOM) text"
# Remove BOM with sed
sed -i '1s/^\xEF\xBB\xBF//' policy.json
# Or with vim
vim -c "set nobomb" -c "wq" policy.json
# Or recreate without BOM
cat policy.json | sed '1s/^\xEF\xBB\xBF//' > policy_clean.json
mv policy_clean.json policy.jsonIf you must use heredoc JSON (not recommended):
# Use jsonencode instead, but if you must:
resource "aws_iam_role" "main" {
assume_role_policy = <<-POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
POLICY
}Escape Terraform interpolation if needed:
# Double $$ to escape interpolation
policy = <<-POLICY
{
"Statement": [{
"Condition": {
"StringLike": {
"s3:prefix": "$${aws:username}/*"
}
}
}]
}
POLICY| Mistake | Invalid | Valid |
|---|---|---|
| Trailing comma | {"a": 1,} | {"a": 1} |
| Single quotes | {'a': 1} | {"a": 1} |
| Unquoted keys | {a: 1} | {"a": 1} |
| Comments | {"a": 1 // comment} | {"a": 1} |
| Trailing comma in array | [1, 2,] | [1, 2] |
| Unescaped backslash | "C:\path" | "C:\\path" |
| Single backslash-n | "line\nbreak" | "line\\nbreak" or actual escaped newline |
file policy.json)jq . file.json validate successfully?jsonencode()?aws_iam_policy_document?jsonencode() — it produces valid JSON by constructionaws_iam_policy_document for IAM policies — HCL syntax with automatic JSON outputjq . file.json to your pipelinetflint and terraform validate catch some JSON issuesJSON parsing errors in Terraform almost always come from hand-written JSON with syntax mistakes. The fix is simple: use jsonencode() to generate JSON from HCL objects, or use provider-specific data sources like aws_iam_policy_document. If you must use raw JSON files, validate them with jq before referencing them in your config.
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.