TerraformPilot

Troubleshooting

Fix: Error Putting S3 Bucket Policy - MalformedPolicy

Fix S3 bucket policy malformed errors in Terraform. Covers JSON syntax, principal format, resource ARN patterns, condition keys, and policy validation.

LLuca Berton1 min read

Quick Answer

#

The S3 bucket policy JSON is invalid — wrong principal format, missing resource ARN, invalid action name, or JSON syntax error. Use aws_iam_policy_document data source instead of hand-written JSON to avoid formatting issues.

The Error

#
Error: putting S3 Bucket Policy (my-bucket):
  MalformedPolicy: Invalid principal in policy
Error: putting S3 Bucket Policy:
  MalformedPolicy: Statement must specify a 'Resource'
Error: putting S3 Bucket Policy:
  MalformedPolicy: Action does not apply to any resource(s) in statement

What Causes This Error

#

1. Invalid Principal Format

#
// BAD — wrong format
"Principal": "123456789012"
 
// GOOD — account ID needs AWS: prefix
"Principal": {"AWS": "arn:aws:iam::123456789012:root"}

2. Missing or Wrong Resource ARN

#

S3 policies need both bucket and object ARNs:

// BAD — only bucket ARN (GetObject needs object ARN)
"Resource": "arn:aws:s3:::my-bucket"
 
// GOOD — both bucket and object ARNs
"Resource": [
  "arn:aws:s3:::my-bucket",
  "arn:aws:s3:::my-bucket/*"
]

3. JSON Syntax Errors

#

Trailing commas, missing quotes, or double-encoded JSON from jsonencode() inside heredoc.

How to Fix It

# #
data "aws_iam_policy_document" "bucket_policy" {
  # Allow CloudFront to read objects
  statement {
    sid     = "AllowCloudFront"
    effect  = "Allow"
    actions = ["s3:GetObject"]
 
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }
 
    resources = ["${aws_s3_bucket.main.arn}/*"]
 
    condition {
      test     = "StringEquals"
      variable = "AWS:SourceArn"
      values   = [aws_cloudfront_distribution.main.arn]
    }
  }
}
 
resource "aws_s3_bucket_policy" "main" {
  bucket = aws_s3_bucket.main.id
  policy = data.aws_iam_policy_document.bucket_policy.json
}

Solution 2: Fix Hand-Written JSON with jsonencode

#
resource "aws_s3_bucket_policy" "main" {
  bucket = aws_s3_bucket.main.id
 
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "${aws_s3_bucket.main.arn}/*"
      },
      {
        Sid       = "DenyInsecureTransport"
        Effect    = "Deny"
        Principal = "*"
        Action    = "s3:*"
        Resource = [
          aws_s3_bucket.main.arn,
          "${aws_s3_bucket.main.arn}/*"
        ]
        Condition = {
          Bool = {
            "aws:SecureTransport" = "false"
          }
        }
      }
    ]
  })
}

Solution 3: Validate Policy Before Applying

#
# Validate using AWS CLI
aws s3api put-bucket-policy --bucket my-bucket \
  --policy file://policy.json --dry-run 2>&1
 
# Or use IAM policy simulator
aws iam simulate-custom-policy \
  --policy-input-list file://policy.json \
  --action-names s3:GetObject \
  --resource-arns "arn:aws:s3:::my-bucket/*"

Common S3 Policy Mistakes

#
MistakeFix
"Principal": "12345""Principal": {"AWS": "arn:aws:iam::12345:root"}
Missing /* in ResourceAdd "arn:aws:s3:::bucket/*" for object operations
s3:List* without bucket ARNAdd "arn:aws:s3:::bucket" (no /*)
Trailing comma in JSONUse jsonencode() to avoid
Double-encoded JSONDon't nest jsonencode() inside heredoc

Troubleshooting Checklist

#
  1. ✅ Is the Principal format correct? (Must be ARN, *, or Service)
  2. ✅ Does Resource include both bucket and object ARNs?
  3. ✅ Do Actions match the Resource type? (GetObject → /*, ListBucket → no /*)
  4. ✅ Is the JSON valid? (Use jsonencode() or validate with CLI)
  5. ✅ Are Condition keys spelled correctly?

Prevention Tips

#
  • Always use aws_iam_policy_document — prevents JSON syntax errors entirely
  • Include both bucket and object ARNs in Resource for mixed operations
  • Use jsonencode() if you must write inline JSON
  • Validate policies with the AWS CLI before applying
#

Conclusion

#

MalformedPolicy errors come from invalid JSON, wrong principal formats, or mismatched resource ARNs. Use the aws_iam_policy_document data source to generate valid policies automatically, or use jsonencode() to avoid JSON syntax issues.

#Terraform#AWS#Troubleshooting#Error Fix

Share this article