Managing AWS IAM Roles and Policies Using Terraform

Learn how to create and manage AWS IAM roles, policies, and users using Terraform for secure access management

Managing AWS IAM Roles and Policies Using Terraform

AWS Identity and Access Management (IAM) is crucial for securing your AWS infrastructure. This guide demonstrates how to manage IAM resources using Terraform, ensuring consistent and version-controlled access management.

Video Tutorial

Learn more about managing AWS IAM with Terraform in this comprehensive video tutorial:

Prerequisites

  • AWS CLI installed and configured
  • Terraform installed (version 1.0.0 or later)
  • Basic understanding of AWS IAM concepts
  • Text editor of your choice

Project Structure

iam-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Setting Up the Provider

Create main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# IAM User
resource "aws_iam_user" "developer" {
  name = "developer-${var.environment}"
  path = "/users/"

  tags = {
    Environment = var.environment
    Role        = "Developer"
  }
}

# IAM Group
resource "aws_iam_group" "developers" {
  name = "developers-${var.environment}"
  path = "/groups/"
}

# Group Membership
resource "aws_iam_user_group_membership" "developer_group" {
  user   = aws_iam_user.developer.name
  groups = [aws_iam_group.developers.name]
}

# IAM Role for EC2
resource "aws_iam_role" "ec2_role" {
  name = "ec2-role-${var.environment}"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })

  tags = {
    Environment = var.environment
  }
}

# S3 Access Policy
resource "aws_iam_policy" "s3_access" {
  name        = "s3-access-${var.environment}"
  description = "Policy for S3 bucket access"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:ListBucket",
          "s3:GetObject",
          "s3:PutObject"
        ]
        Resource = [
          "arn:aws:s3:::${var.bucket_name}",
          "arn:aws:s3:::${var.bucket_name}/*"
        ]
      }
    ]
  })
}

# Attach policy to role
resource "aws_iam_role_policy_attachment" "s3_access" {
  policy_arn = aws_iam_policy.s3_access.arn
  role       = aws_iam_role.ec2_role.name
}

# Instance Profile
resource "aws_iam_instance_profile" "ec2_profile" {
  name = "ec2-profile-${var.environment}"
  role = aws_iam_role.ec2_role.name
}

# Developer Group Policy
resource "aws_iam_group_policy" "developer_policy" {
  name  = "developer-policy-${var.environment}"
  group = aws_iam_group.developers.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ec2:Describe*",
          "s3:List*",
          "s3:Get*"
        ]
        Resource = "*"
      }
    ]
  })
}

# Password Policy
resource "aws_iam_account_password_policy" "strict" {
  minimum_password_length        = 12
  require_lowercase_characters   = true
  require_uppercase_characters   = true
  require_numbers               = true
  require_symbols               = true
  allow_users_to_change_password = true
  password_reuse_prevention     = 24
  max_password_age             = 90
}

Variables Configuration

Create variables.tf:

variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"
}

variable "bucket_name" {
  description = "Name of the S3 bucket to grant access to"
  type        = string
}

Output Configuration

Create outputs.tf:

output "iam_user_arn" {
  description = "ARN of the created IAM user"
  value       = aws_iam_user.developer.arn
}

output "iam_role_arn" {
  description = "ARN of the created IAM role"
  value       = aws_iam_role.ec2_role.arn
}

output "instance_profile_arn" {
  description = "ARN of the created instance profile"
  value       = aws_iam_instance_profile.ec2_profile.arn
}

Best Practices

  1. Principle of Least Privilege

    • Grant minimum necessary permissions
    • Use specific resource ARNs instead of wildcards
    • Regularly review and audit permissions
  2. Security

    • Implement strong password policies
    • Enable MFA for IAM users
    • Rotate access keys regularly
  3. Organization

    • Use consistent naming conventions
    • Tag resources appropriately
    • Document policy decisions

Common IAM Patterns

1. Cross-Account Access

resource "aws_iam_role" "cross_account" {
  name = "cross-account-access"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::ACCOUNT-ID:root"
        }
      }
    ]
  })
}

2. Service-Linked Roles

resource "aws_iam_service_linked_role" "es" {
  aws_service_name = "es.amazonaws.com"
}

3. Custom Policy with Conditions

resource "aws_iam_policy" "restricted_access" {
  name = "restricted-access"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = ["s3:GetObject"]
        Resource = "*"
        Condition = {
          IpAddress = {
            "aws:SourceIp": ["10.0.0.0/8"]
          }
        }
      }
    ]
  })
}

Deployment Steps

  1. Initialize Terraform:
terraform init
  1. Create terraform.tfvars:
aws_region   = "us-west-2"
environment  = "dev"
bucket_name  = "my-application-bucket"
  1. Review the plan:
terraform plan
  1. Apply the configuration:
terraform apply

Security Considerations

  1. Access Key Management

    • Don’t create access keys using Terraform
    • Manage access keys manually through AWS Console
    • Implement key rotation policies
  2. Policy Versioning

    • Keep policy documents in version control
    • Document policy changes
    • Use policy variables for reusability
  3. Monitoring and Auditing

    • Enable AWS CloudTrail
    • Set up alerts for policy changes
    • Regular security reviews

Cleanup

Remove the IAM resources when no longer needed:

terraform destroy

Troubleshooting

  1. Policy Validation Errors

    • Check JSON syntax
    • Verify action names
    • Confirm resource ARNs
  2. Role Trust Issues

    • Verify assume role policies
    • Check principal formatting
    • Confirm account IDs
  3. Permission Issues

    • Review effective permissions
    • Check policy attachments
    • Verify group memberships

Conclusion

You’ve learned how to set up and manage AWS IAM using Terraform. This setup provides:

  • Consistent access management
  • Version-controlled policies
  • Reproducible IAM configurations
  • Secure access patterns

Remember to:

  • Follow security best practices
  • Regularly review and update policies
  • Document your IAM structure
  • Maintain proper access controls