Table of Contents

The Error

Error creating IAM Role: MalformedPolicyDocument: Invalid principal in policy

What Causes This

The IAM assume role policy (trust relationship) contains invalid JSON, references a non-existent principal, or has syntax errors. Common mistakes include using the wrong ARN format, missing quotes, or referencing deleted accounts/services.

How to Fix It

Solution 1: Fix the Trust Policy JSON

# BAD — common mistakes
resource "aws_iam_role" "lambda" {
  name = "lambda-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = {
        Service = "lambda"  # Wrong! Missing .amazonaws.com
      }
    }]
  })
}

# GOOD — correct service principal
resource "aws_iam_role" "lambda" {
  name = "lambda-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"  # Correct!
      }
    }]
  })
}

Solution 2: Validate JSON Before Applying

# Check your policy is valid JSON
terraform console
> jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
      Action = "sts:AssumeRole"
    }]
  })

Common Service Principals

# Lambda:        lambda.amazonaws.com
# EC2:           ec2.amazonaws.com
# ECS Tasks:     ecs-tasks.amazonaws.com
# API Gateway:   apigateway.amazonaws.com
# CloudWatch:    events.amazonaws.com
# S3:            s3.amazonaws.com
# SNS:           sns.amazonaws.com
# CodeBuild:     codebuild.amazonaws.com
# CodePipeline:  codepipeline.amazonaws.com

Solution 3: Cross-Account Trust

resource "aws_iam_role" "cross_account" {
  name = "cross-account-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = {
        AWS = "arn:aws:iam::123456789012:root"  # Account ID must exist!
      }
      Action    = "sts:AssumeRole"
    }]
  })
}

Prevention Tips

  1. Pin provider versions — avoid surprise breaking changes
  2. Use CI/CD — catch errors before they hit production
  3. Test with terraform plan — always review before applying
  4. Keep Terraform updated — newer versions have better error messages
  5. Use terraform validate — catches syntax errors early

Hands-On Courses

Learn to avoid these errors with interactive, project-based courses:

Conclusion

This error is common and fixable. Follow the solutions above, and check our Terraform course for hands-on training that covers real-world troubleshooting scenarios.