In the fast-paced world of tech startups, securing cloud infrastructure is critical. In this post, I'll walk you through a practical example of building AWS IAM permissions using Terraform, where we structured user access for different groups—Developers, Operations, Finance, and Analysts—in an example startup scenario.
Background:
Imagine you're a cloud engineer at a startup that just launched its first fitness-tracking application. AWS was set up quickly to meet launch deadlines, and now it's time to address technical debt that’s accrued. Each team needs specific permissions, such as managing EC2, accessing CloudWatch logs, or viewing cost reports.
We’ll use Terraform, an Infrastructure-as-Code tool, to automate the process and ensure repeatability.
Step 1: Enforce Strong Security Policies
1. Password Policy: To ensure all users adhere to strong password requirements, we defined an account-wide password policy:
resource "aws_iam_account_password_policy" "strong_password_policy" {
minimum_password_length = 12
require_symbols = true
require_numbers = true
require_uppercase_characters = true
require_lowercase_characters = true
allow_users_to_change_password = true
max_password_age = 90
password_reuse_prevention = 5
}
This policy enforces security while providing flexibility for users to manage their credentials.
2. MFA Enforcement: To add an extra layer of protection, we enforced MFA for all users:
resource "aws_iam_policy" "mfa_enforcement" {
name = "MFAEnforcementPolicy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Deny",
Action = "*",
Resource = "*",
Condition = {
Bool: {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
})
}
We attached this policy to all IAM groups to ensure compliance.
Step 2: Role-Based Access with Terraform
Developers: Developers need access to manage EC2 instances, S3 buckets, and CloudWatch logs. Here's how we implemented it:
resource "aws_iam_policy" "developers_policy" {
name = "DevelopersPolicy"
policy = jsonencode({
Statement = [
{
Effect = "Allow",
Action = ["ec2:DescribeInstances", "ec2:StartInstances", "ec2:StopInstances"],
Resource = "*"
},
{
Effect = "Allow",
Action = ["s3:ListBucket", "s3:GetObject"],
Resource = ["arn:aws:s3:::your-bucket-name", "arn:aws:s3:::your-bucket-name/*"]
},
{
Effect = "Allow",
Action = ["logs:DescribeLogGroups", "logs:GetLogEvents"],
Resource = "*"
}
]
})
}
resource "aws_iam_group_policy_attachment" "developers_policy_attachment" {
group = aws_iam_group.developers_group.name
policy_arn = aws_iam_policy.developers_policy.arn
}
Operations: The operations team needs broader access, including Systems Manager and RDS:
resource "aws_iam_policy" "operations_policy" {
name = "OperationsPolicy"
policy = jsonencode({
Statement = [
{ "Effect": "Allow", "Action": ["ec2:*"], "Resource": "*" },
{ "Effect": "Allow", "Action": ["cloudwatch:*"], "Resource": "*" },
{ "Effect": "Allow", "Action": ["ssm:*"], "Resource": "*" },
{ "Effect": "Allow", "Action": ["rds:*"], "Resource": "*" }
]
})
}
Finance: For finance, we provided read-only access to billing-related services:
resource "aws_iam_policy" "finance_policy" {
name = "FinancePolicy"
policy = jsonencode({
Statement = [
{ "Effect": "Allow", "Action": ["ce:GetCostAndUsage"], "Resource": "*" },
{ "Effect": "Allow", "Action": ["budgets:ViewBudget"], "Resource": "*" },
{ "Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetObject"], "Resource": "*" }
]
})
}
Analysts: Analysts required read-only access to databases and S3 buckets:
resource "aws_iam_policy" "analysts_policy" {
name = "AnalystsPolicy"
policy = jsonencode({
Statement = [
{ "Effect": "Allow", "Action": ["s3:GetObject", "s3:ListBucket"], "Resource": "*" },
{ "Effect": "Allow", "Action": ["rds:Describe*"], "Resource": "*" }
]
})
}
Step 3: Automating with Terraform
Using Terraform's for_each
, we dynamically created IAM users and assigned them to groups:
variable "developer_names" {
default = ["developer1", "developer2"]
}
resource "aws_iam_user" "developers" {
for_each = toset(var.developer_names)
name = each.value
}
resource "aws_iam_user_group_membership" "developers_group" {
name = "developers-membership"
group = aws_iam_group.developers_group.name
users = [for user in aws_iam_user.developers : user.name]
}
This approach minimizes duplication and allows easy scaling. We’ll do this for each respective group.
Step 4: Deployment Process
Initialize Terraform:
terraform init
Validate Configuration:
terraform validate
Plan and Apply:
terraform plan terraform apply
Results:
With this setup:
Developers can manage EC2 and S3 for their applications.
Operations has full control over infrastructure.
Finance has secure, read-only billing access.
Analysts have the data access they need without overreaching permissions.
Conclusion:
Using Terraform to manage IAM permissions ensures a scalable, repeatable, and secure workflow for cloud infrastructure. By assigning role-based access, we’ve met the startup’s security needs while empowering teams with the right tools.
Want to secure your cloud infrastructure? Start implementing IAM permissions with Terraform today!