Table of Contents
IAM With Terraform: Security-First Approach
IAM is the foundation of AWS security. Getting it right in Terraform means following the principle of least privilege and making permissions auditable.
IAM Role With Policy
# Role with trust policy
resource "aws_iam_role" "lambda" {
name = "lambda-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}]
})
}
# Managed policy attachment
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
# Custom inline policy
resource "aws_iam_role_policy" "s3_access" {
name = "s3-read-access"
role = aws_iam_role.lambda.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["s3:GetObject", "s3:ListBucket"]
Resource = [
aws_s3_bucket.data.arn,
"${aws_s3_bucket.data.arn}/*"
]
}]
})
}
IAM Policy Document Data Source
For complex policies, use the aws_iam_policy_document data source:
data "aws_iam_policy_document" "s3_policy" {
statement {
effect = "Allow"
actions = ["s3:GetObject"]
resources = ["${aws_s3_bucket.data.arn}/*"]
condition {
test = "StringEquals"
variable = "aws:RequestedRegion"
values = ["us-east-1"]
}
}
}
resource "aws_iam_policy" "s3_read" {
name = "s3-read-policy"
policy = data.aws_iam_policy_document.s3_policy.json
}
Cross-Account Role
resource "aws_iam_role" "cross_account" {
name = "cross-account-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::123456789012:root"
}
Action = "sts:AssumeRole"
Condition = {
StringEquals = {
"sts:ExternalId" = var.external_id
}
}
}]
})
}
Best Practices
- Least privilege — grant only the permissions needed
- Use roles, not users — for services and CI/CD
- Use
jsonencode()— over heredoc JSON for policy documents - Separate policies by concern — one policy per service/action group
- Use conditions — restrict by IP, region, or MFA
- Avoid wildcards —
"Resource": "*"is almost always too broad - Audit with Access Analyzer — find unused permissions
Learn More
- Terraform for Beginners Course — hands-on IAM labs
- Terraform By Example Book — real-world patterns
- Terraform Cheat Sheet — quick command reference



